Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

numpydoc style(signac/contrib/errors.py, signac/contrib/filesystems.py, signac/contrib/filterparser.py, signac/contrib/hashing.py) #322

Merged
merged 20 commits into from
May 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ max-line-length = 100
exclude = configobj,passlib,cite.py,conf.py

[pydocstyle]
match = (jsondict|project|schema|job|linked_view|utility).py
match = (jsondict|project|schema|job|errors|filterparse|hashing|linked_view|utility).py
ignore = D105, D107, D203, D204, D213

[mypy]
Expand Down
8 changes: 8 additions & 0 deletions signac/common/errors.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
# Copyright (c) 2017 The Regents of the University of Michigan
# All rights reserved.
# This software is licensed under the BSD 3-Clause License.
"""Errors raised by signac.common classes."""
from ..core.errors import Error


class ConfigError(Error, RuntimeError):
"""Error with parsing or reading a configuration file."""
pass


# this class is only used by deprecated features
class AuthenticationError(Error, RuntimeError):
"""Authentication error."""

def __str__(self):
if len(self.args) > 0:
Expand All @@ -18,9 +22,13 @@ def __str__(self):
return "Failed to authenticate with host."


# this class is only used by deprecated features
class ExportError(Error, RuntimeError):
"""Error exporting documents to a mirror."""
pass


# this class is only used by deprecated features
class FetchError(FileNotFoundError):
"""Error in fetching data."""
pass
36 changes: 28 additions & 8 deletions signac/contrib/errors.py
Original file line number Diff line number Diff line change
@@ -1,38 +1,58 @@
# Copyright (c) 2017 The Regents of the University of Michigan
# All rights reserved.
# This software is licensed under the BSD 3-Clause License.
"""Errors raised by signac.contrib classes."""

from ..core.errors import Error


class WorkspaceError(Error, OSError):
"Raised when there is an issue creating or accessing the workspace."
"""Raised when there is an issue creating or accessing the workspace.

Parameters
----------
error :
The underlying error causing this issue.

"""
def __init__(self, error):
self.error = error
"The underlying error causing this issue."

def __str__(self):
return self.error


class DestinationExistsError(Error, RuntimeError):
"The destination for a move or copy operation already exists."
"""The destination for a move or copy operation already exists.

Parameters
----------
destination :
The destination object causing the error.

"""
def __init__(self, destination):
self.destination = destination
"The destination object causing the error."


class JobsCorruptedError(Error, RuntimeError):
"The state point manifest file of one or more jobs cannot be opened or is corrupted."
"""The state point manifest file of one or more jobs cannot be opened or is corrupted.

Parameters
----------
job_ids :
The job id(s) of the corrupted job(s).

"""
def __init__(self, job_ids):
self.job_ids = job_ids
"The job id(s) of the corrupted job(s)."


class StatepointParsingError(Error, RuntimeError):
"Indicates an error that occurred while trying to identify a state point."
"""Indicates an error that occurred while trying to identify a state point."""
pass


class IncompatibleSchemaVersion(Error):
"The project's schema version is incompatible with this version of signac."
"""The project's schema version is incompatible with this version of signac."""
pass
134 changes: 132 additions & 2 deletions signac/contrib/filterparse.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,115 @@
# Copyright (c) 2017 The Regents of the University of Michigan
# All rights reserved.
# This software is licensed under the BSD 3-Clause License.
"""Parse the filter arguments."""
bdice marked this conversation as resolved.
Show resolved Hide resolved

import sys
from ..core import json


def _print_err(msg=None):
"""Print the provided message to stderr.

Parameters
----------
msg : str
Error message to be printed (Default value = None).

"""
print(msg, file=sys.stderr)


def _with_message(query, file):
"""Print the interpreted filter arguments to the provided file.

Parameters
----------
query : dict
Filter arguments.
file :
The file where the filter interpretation is printed.

Returns
-------
query : dict
Filter arguments.

"""
print("Interpreted filter arguments as '{}'.".format(json.dumps(query)), file=file)
return query


def _read_index(project, fn_index=None):
"""Read index from the file passed.

Parameters
----------
project : :class:`~signac.Project`
Project handle.

fn_index : str
File name of the index (Default value = None).

Returns
-------
generator
Returns the file contents, parsed as JSON-encoded lines.

"""
if fn_index is not None:
_print_err("Reading index from file '{}'...".format(fn_index))
fd = open(fn_index)
return (json.loads(l) for l in fd)


def _is_json(q):
"""Check if q is JSON.

Parameters
----------
q : str
Query string.

Returns
-------
bool
True if q starts with "{" and ends with "}".

"""
return q.strip().startswith('{') and q.strip().endswith('}')


def _is_regex(q):
"""Check if q is a regular expression.

Parameters
----------
q : str
Query string.

Returns
-------
bool
True if q starts with "/" and ends with "/".

"""
return q.startswith('/') and q.endswith('/')


def _parse_json(q):
"""Parse a query argument as JSON.

Parameters
----------
q : json
Query argument.

Raises
------
JSONDecodeError
Raised if the input cannot be parsed as JSON.

"""
try:
return json.loads(q)
except json.JSONDecodeError:
Expand All @@ -53,7 +133,19 @@ def _parse_json(q):


def _cast(x):
"Attempt to interpret x with the correct type."
"""Attempt to interpret x with the correct type.

Parameters
----------
x : str
The value to cast.

Returns
-------
object
Value of x, cast from a str to an appropriate type (bool, NoneType, int, float, str).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what to put here. Can you find an example of a NumPy docstring that doesn't have a specified return type?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is the example for different type return in a methods that I following. link

Copy link
Member

@bdice bdice May 7, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately that example is for a slightly different case. That would apply if _cast returned two values. This function doesn't have a defined return type, so it should claim its return type is object. I will make a suggestion above.

"""
try:
if x in CAST_MAPPING_WARNING:
print("Did you mean {}?".format(CAST_MAPPING_WARNING[x]), file=sys.stderr)
Expand All @@ -69,6 +161,28 @@ def _cast(x):


def _parse_simple(key, value=None):
"""Parse simple search syntax.

Parameters
----------
key : str
The filter key.

value :
The filter value. If None, the filter returns
True if the provided key exists (Default value = None).

Returns
-------
dict
bdice marked this conversation as resolved.
Show resolved Hide resolved
Parsed filter arguments.

Raises
------
ValueError
If filter arguments have an invalid key.

"""
if value is None or value == '!':
return {key: {'$exists': True}}
elif _is_json(value):
Expand All @@ -78,12 +192,28 @@ def _parse_simple(key, value=None):
elif _is_json(key):
raise ValueError(
"Please check your filter arguments. "
"Using as JSON expression as key is not allowed: '{}'.".format(key))
"Using a JSON expression as a key is not allowed: '{}'.".format(key))
else:
return {key: _cast(value)}


def parse_filter_arg(args, file=sys.stderr):
"""Parse a series of filter arguments into a dictionary.

Parameters
----------
args : sequence of str
Filter arguments to parse.

file :
The file to write message (Default value = sys.stderr).

Returns
-------
dict
Filter arguments.

"""
if args is None or len(args) == 0:
return None
elif len(args) == 1:
Expand Down
23 changes: 20 additions & 3 deletions signac/contrib/hashing.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,31 @@
# Copyright (c) 2017 The Regents of the University of Michigan
# All rights reserved.
# This software is licensed under the BSD 3-Clause License.
"""Hashing functions."""

import hashlib
# we need to use the standard module here to ensure
# exact consistent formatting
import json
# We must use the standard library json for exact consistency in formatting


def calc_id(spec):
"Calculate and return a hash value for the given spec."
"""Calculate and return a hash value for the given spec.

ac-optimus marked this conversation as resolved.
Show resolved Hide resolved
The hash is computed as an MD5 checksum of the input data. The input data
is first encoded as JSON, with dictionary keys sorted to ensure the hash
is reproducible.

Parameters
----------
spec : dict
A JSON-encodable mapping.

Returns
-------
str
Encoded hash in hexadecimal format.

"""
blob = json.dumps(spec, sort_keys=True)
m = hashlib.md5()
m.update(blob.encode())
Expand Down
2 changes: 2 additions & 0 deletions signac/core/errors.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# Copyright (c) 2017 The Regents of the University of Michigan
# All rights reserved.
# This software is licensed under the BSD 3-Clause License.
"""Errors raised by signac.core classes."""


class Error(Exception):
"""Base class used for signac Errors."""
pass
13 changes: 9 additions & 4 deletions signac/errors.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# Copyright (c) 2017 The Regents of the University of Michigan
# All rights reserved.
# This software is licensed under the BSD 3-Clause License.
"""Errors raised by signac."""
bdice marked this conversation as resolved.
Show resolved Hide resolved

# The subpackage error modules (e.g. signac.core.errors) are used to bundle
# exceptions that are relevant beyond a single module. This top-level errors
# module is used to expose user-facing exception classes.

from .core.errors import Error

Expand All @@ -18,12 +23,12 @@


class SyncConflict(Error, RuntimeError):
"Raised when a synchronization operation fails."
"""Raised when a synchronization operation fails."""
pass


class FileSyncConflict(SyncConflict):
"Raised when a synchronization operation fails due to a file conflict."
"""Raised when a synchronization operation fails due to a file conflict."""
def __init__(self, filename):
self.filename = filename
"The filename of the file that caused the conflict."
Expand All @@ -33,7 +38,7 @@ def __str__(self):


class DocumentSyncConflict(SyncConflict):
"Raised when a synchronization operation fails due to a document conflict."
"""Raised when a synchronization operation fails due to a document conflict."""
def __init__(self, keys):
self.keys = keys
"The keys that caused the conflict."
Expand All @@ -43,7 +48,7 @@ def __str__(self):


class SchemaSyncConflict(SyncConflict):
"Raised when a synchronization operation fails due to schema differences."
"""Raised when a synchronization operation fails due to schema differences."""
def __init__(self, schema_src, schema_dst):
self.schema_src = schema_src
self.schema_dst = schema_dst
Expand Down