Skip to content

Commit e2029b4

Browse files
author
Bas van Beek
committed
First push
1 parent e8253d7 commit e2029b4

25 files changed

+2230
-2
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,6 @@ venv.bak/
102102

103103
# mypy
104104
.mypy_cache/
105+
106+
.DS_Store
107+
.vscode/

CHANGELOG.rst

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
##########
2+
Change Log
3+
##########
4+
5+
All notable changes to this project will be documented in this file.
6+
This project adheres to `Semantic Versioning <http://semver.org/>`_.
7+
8+
9+
[Unreleased]
10+
************
11+
12+
Added
13+
-----
14+
15+
* Empty Python project directory structure.

CITATION.cff

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# YAML 1.2
2+
# Metadata for citation of this software according to the CFF format (https://citation-file-format.github.io/)
3+
cff-version: 1.0.3
4+
message: If you use this software, please cite it as below.
5+
title: AssertionLib
6+
authors:
7+
- given-names: Bas
8+
family-names: van Beek
9+
orcid: "https://orcid.org/0000-0003-2463-6559"
10+
11+
keywords:
12+
- python
13+
version: '0.0.1'
14+
date-released: 2019-10-10
15+
repository-code: https://github.com/nlesc-nano/AssertionLib
16+
license: "LGPL-3.0"

CODE_OF_CONDUCT.rst

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
###############################################################################
2+
Contributor Covenant Code of Conduct
3+
###############################################################################
4+
5+
Our Pledge
6+
**********
7+
8+
In the interest of fostering an open and welcoming environment, we as
9+
contributors and maintainers pledge to making participation in our project and
10+
our community a harassment-free experience for everyone, regardless of age, body
11+
size, disability, ethnicity, gender identity and expression, level of experience,
12+
education, socio-economic status, nationality, personal appearance, race,
13+
religion, or sexual identity and orientation.
14+
15+
Our Standards
16+
*************
17+
18+
Examples of behavior that contributes to creating a positive environment
19+
include:
20+
21+
* Using welcoming and inclusive language
22+
* Being respectful of differing viewpoints and experiences
23+
* Gracefully accepting constructive criticism
24+
* Focusing on what is best for the community
25+
* Showing empathy towards other community members
26+
27+
Examples of unacceptable behavior by participants include:
28+
29+
* The use of sexualized language or imagery and unwelcome sexual attention or
30+
advances
31+
* Trolling, insulting/derogatory comments, and personal or political attacks
32+
* Public or private harassment
33+
* Publishing others' private information, such as a physical or electronic
34+
address, without explicit permission
35+
* Other conduct which could reasonably be considered inappropriate in a
36+
professional setting
37+
38+
Our Responsibilities
39+
********************
40+
41+
Project maintainers are responsible for clarifying the standards of acceptable
42+
behavior and are expected to take appropriate and fair corrective action in
43+
response to any instances of unacceptable behavior.
44+
45+
Project maintainers have the right and responsibility to remove, edit, or
46+
reject comments, commits, code, wiki edits, issues, and other contributions
47+
that are not aligned to this Code of Conduct, or to ban temporarily or
48+
permanently any contributor for other behaviors that they deem inappropriate,
49+
threatening, offensive, or harmful.
50+
51+
Scope
52+
*****
53+
54+
This Code of Conduct applies both within project spaces and in public spaces
55+
when an individual is representing the project or its community. Examples of
56+
representing a project or community include using an official project e-mail
57+
address, posting via an official social media account, or acting as an appointed
58+
representative at an online or offline event. Representation of a project may be
59+
further defined and clarified by project maintainers.
60+
61+
Enforcement
62+
***********
63+
64+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
65+
reported by contacting the project team at [email protected]. All
66+
complaints will be reviewed and investigated and will result in a response that
67+
is deemed necessary and appropriate to the circumstances. The project team is
68+
obligated to maintain confidentiality with regard to the reporter of an incident.
69+
Further details of specific enforcement policies may be posted separately.
70+
71+
Project maintainers who do not follow or enforce the Code of Conduct in good
72+
faith may face temporary or permanent repercussions as determined by other
73+
members of the project's leadership.
74+
75+
Attribution
76+
***********
77+
78+
This Code of Conduct is adapted from the `Contributor Covenant <https://www.contributor-covenant.org>`_, version 1.4,
79+
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html

CONTRIBUTING.rst

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
############################
2+
Contributing guidelines
3+
############################
4+
5+
We welcome any kind of contribution to our software, from simple comment or question to a full fledged `pull request <https://help.github.com/articles/about-pull-requests/>`_. Please read and follow our `Code of Conduct <CODE_OF_CONDUCT.rst>`_.
6+
7+
A contribution can be one of the following cases:
8+
9+
1. you have a question;
10+
1. you think you may have found a bug (including unexpected behavior);
11+
1. you want to make some kind of change to the code base (e.g. to fix a bug, to add a new feature, to update documentation).
12+
13+
The sections below outline the steps in each case.
14+
15+
You have a question
16+
*******************
17+
18+
1. use the search functionality `here <https://github.com/nlesc-nano/AssertionLib/issues>`__ to see if someone already filed the same issue;
19+
1. if your issue search did not yield any relevant results, make a new issue;
20+
1. apply the "Question" label; apply other labels when relevant.
21+
22+
You think you may have found a bug
23+
**********************************
24+
25+
1. use the search functionality `here <https://github.com/nlesc-nano/AssertionLib/issues>`__ to see if someone already filed the same issue;
26+
1. if your issue search did not yield any relevant results, make a new issue, making sure to provide enough information to the rest of the community to understand the cause and context of the problem. Depending on the issue, you may want to include:
27+
- the `SHA hashcode <https://help.github.com/articles/autolinked-references-and-urls/#commit-shas>`_ of the commit that is causing your problem;
28+
- some identifying information (name and version number) for dependencies you're using;
29+
- information about the operating system;
30+
1. apply relevant labels to the newly created issue.
31+
32+
You want to make some kind of change to the code base
33+
*****************************************************
34+
35+
1. (**important**) announce your plan to the rest of the community *before you start working*. This announcement should be in the form of a (new) issue;
36+
1. (**important**) wait until some kind of consensus is reached about your idea being a good idea;
37+
1. if needed, fork the repository to your own Github profile and create your own feature branch off of the latest master commit. While working on your feature branch, make sure to stay up to date with the master branch by pulling in changes, possibly from the 'upstream' repository (follow the instructions `here <https://help.github.com/articles/configuring-a-remote-for-a-fork/>`__ and `here <https://help.github.com/articles/syncing-a-fork/>`__);
38+
1. make sure the existing tests still work by running ``python setup.py test``;
39+
1. add your own tests (if necessary);
40+
1. update or expand the documentation;
41+
1. `push <http://rogerdudler.github.io/git-guide/>`_ your feature branch to (your fork of) the Compound Attachment/Analysis Tool repository on GitHub;
42+
1. create the pull request, e.g. following the instructions `here <https://help.github.com/articles/creating-a-pull-request/>`__.
43+
44+
In case you feel like you've made a valuable contribution, but you don't know how to write or run tests for it, or how to generate the documentation: don't let this discourage you from making the pull request; we can help you! Just go ahead and submit the pull request, but keep in mind that you might be asked to append additional commits to your pull request.

MANIFEST.in

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
include LICENSE
2+
include README.rst

NOTICE

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
This product includes AssertionLib, software developed by Bas van Beek
2+
.

README.md

-2
This file was deleted.

README.rst

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
.. image:: https://img.shields.io/badge/python-3.6-blue.svg
2+
:target: https://www.python.org
3+
.. image:: https://img.shields.io/badge/python-3.7-blue.svg
4+
:target: https://www.python.org
5+
6+
7+
##################
8+
AssertionLib 0.0.1
9+
##################
10+
11+
A package for performing assertions.
12+
13+
14+
Installation
15+
************
16+
17+
AssertionLib can be installed as following:
18+
19+
* ``pip install git+https://github.com/nlesc-nano/AssertionLib``

assertionlib/__init__.py

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from .__version__ import __version__
2+
3+
from .manager import assertion, AssertionManager
4+
5+
__version__ = __version__
6+
__author__ = "B. F. van Beek"
7+
__email__ = '[email protected]'
8+
9+
__all__ = ['assertion', 'AssertionManager']

assertionlib/__version__.py

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__version__ = '0.0.1'

assertionlib/dataclass.py

+189
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
"""
2+
assertionlib.dataclass
3+
======================
4+
5+
A dataclass with a number of generic pre-defined (magic) methods.
6+
7+
Index
8+
-----
9+
.. currentmodule:: assertionlib.dataclass
10+
.. autosummary::
11+
AbstractDataClass
12+
13+
API
14+
---
15+
.. autoclass:: AbstractDataClass
16+
:members:
17+
:private-members:
18+
:special-members:
19+
20+
"""
21+
22+
import textwrap
23+
from copy import deepcopy
24+
from typing import (Any, Dict, FrozenSet, Iterable, Tuple)
25+
26+
27+
class AbstractDataClass:
28+
"""A dataclass with a number of generic pre-defined (magic) methods."""
29+
30+
#: A :class:`frozenset` with the names of private instance variables.
31+
#: These attributes will be excluded whenever calling :meth:`AbstractDataClass.as_dict`,
32+
#: printing or comparing objects.
33+
_PRIVATE_ATTR: FrozenSet[str] = frozenset()
34+
35+
def __str__(self) -> str:
36+
"""Return a string representation of this instance."""
37+
def _str(k: str, v: Any) -> str:
38+
return f'{k:{width}} = ' + textwrap.indent(repr(v), indent2)[len(indent2):]
39+
40+
width = max(len(k) for k in vars(self))
41+
indent1 = ' ' * 4
42+
indent2 = ' ' * (3 + width)
43+
iterable = self._str_iterator()
44+
ret = ',\n'.join(_str(k, v) for k, v in iterable)
45+
46+
return f'{self.__class__.__name__}(\n{textwrap.indent(ret, indent1)}\n)'
47+
48+
__repr__ = __str__
49+
50+
def _str_iterator(self) -> Iterable[Tuple[str, Any]]:
51+
"""Return an iterable for the :meth:`AbstractDataClass.__str__` method."""
52+
return ((k, v) for k, v in vars(self).items() if k not in self._PRIVATE_ATTR)
53+
54+
def __eq__(self, value: Any) -> bool:
55+
"""Check if this instance is equivalent to **value**."""
56+
if type(self) is not type(value):
57+
return False
58+
59+
try:
60+
for k, v1 in vars(self).items():
61+
if k in self._PRIVATE_ATTR:
62+
continue
63+
v2 = getattr(value, k)
64+
assert v1 == v2
65+
except (AttributeError, AssertionError):
66+
return False
67+
else:
68+
return True
69+
70+
def copy(self, deep: bool = False) -> 'AbstractDataClass':
71+
"""Return a deep or shallow copy of this instance.
72+
73+
Parameters
74+
----------
75+
deep : :class:`bool`
76+
Whether or not to return a deep or shallow copy.
77+
78+
Returns
79+
-------
80+
:class:`AbstractDataClass`
81+
A new instance constructed from this instance.
82+
83+
"""
84+
cls = type(self)
85+
ret = cls.__new__(cls)
86+
ret.__dict__ = vars(self).copy() if not deep else deepcopy(vars(self))
87+
return ret
88+
89+
def __copy__(self) -> 'AbstractDataClass':
90+
"""Return a shallow copy of this instance."""
91+
return self.copy(deep=False)
92+
93+
def __deepcopy__(self, memo=None) -> 'AbstractDataClass':
94+
"""Return a deep copy of this instance."""
95+
return self.copy(deep=True)
96+
97+
def as_dict(self, return_private: bool = False) -> Dict[str, Any]:
98+
"""Construct a dictionary from this instance with all non-private instance variables.
99+
100+
No attributes specified in :data:`AbstractDataClass._PRIVATE_ATTR` will be included in
101+
the to-be returned dictionary.
102+
103+
Parameters
104+
----------
105+
return_private : :class:`bool`
106+
If ``True``, return both public and private instance variables.
107+
Private instance variables are defined in :data:`AbstractDataClass._PRIVATE_ATTR`.
108+
109+
Returns
110+
-------
111+
:class:`dict` [:class:`str`, :class:`object`]
112+
A dictionary of arrays with keyword arguments for initializing a new
113+
instance of this class.
114+
115+
See also
116+
--------
117+
:meth:`AbstractDataClass.from_dict`:
118+
Construct a instance of this objects' class from a dictionary with keyword arguments.
119+
120+
"""
121+
ret = deepcopy(vars(self))
122+
if not return_private:
123+
for key in self._PRIVATE_ATTR:
124+
del ret[key]
125+
return ret
126+
127+
@classmethod
128+
def from_dict(cls, dct: Dict[str, Any]) -> 'AbstractDataClass':
129+
"""Construct a instance of this objects' class from a dictionary with keyword arguments.
130+
131+
Parameters
132+
----------
133+
dct : :class:`dict` [:class:`str`, :class:`.Any`]
134+
A dictionary with keyword arguments for constructing a new
135+
:class:`AbstractDataClass` instance.
136+
137+
Returns
138+
-------
139+
:class:`AbstractDataClass`
140+
A new instance of this object's class constructed from **dct**.
141+
142+
See also
143+
--------
144+
:meth:`AbstractDataClass.as_dict`:
145+
Construct a dictionary from this instance with all non-private instance variables.
146+
147+
"""
148+
return cls(**dct)
149+
150+
@classmethod
151+
def inherit_annotations(cls) -> type:
152+
"""A decorator for inheriting annotations and docstrings.
153+
154+
Can be applied to methods of :class:`AbstractDataClass` subclasses to automatically
155+
inherit the docstring and annotations of identical-named functions of its superclass.
156+
157+
Examples
158+
--------
159+
.. code:: python
160+
161+
>>> class sub_class(AbstractDataClass)
162+
...
163+
... @AbstractDataClass.inherit_annotations()
164+
... def as_dict(self, return_private=False):
165+
... pass
166+
167+
>>> sub_class.as_dict.__doc__ == AbstractDataClass.as_dict.__doc__
168+
True
169+
170+
>>> sub_class.as_dict.__annotations__ == AbstractDataClass.as_dict.__annotations__
171+
True
172+
173+
"""
174+
def decorator(sub_attr: type) -> type:
175+
super_attr = getattr(cls, sub_attr.__name__)
176+
sub_cls_name = sub_attr.__qualname__.split('.')[0]
177+
178+
# Update annotations
179+
if not sub_attr.__annotations__:
180+
sub_attr.__annotations__ = dct = super_attr.__annotations__.copy()
181+
if 'return' in dct and dct['return'] == cls.__name__:
182+
dct['return'] = sub_attr.__qualname__.split('.')[0]
183+
184+
# Update docstring
185+
if sub_attr.__doc__ is None:
186+
sub_attr.__doc__ = super_attr.__doc__.replace(cls.__name__, sub_cls_name)
187+
188+
return sub_attr
189+
return decorator

0 commit comments

Comments
 (0)