[med-svn] [python-dictobj] 01/05: Imported Upstream version 0.4
Olivier Sallou
osallou at debian.org
Mon Jul 4 10:29:14 UTC 2016
This is an automated email from the git hooks/post-receive script.
osallou pushed a commit to branch master
in repository python-dictobj.
commit 4c2547c569d012c7a49448606740fd2292685ad8
Author: Olivier Sallou <olivier.sallou at debian.org>
Date: Mon Jul 4 12:11:02 2016 +0200
Imported Upstream version 0.4
---
.gitignore | 6 +
CHANGELOG.md | 73 +++++++++++
LICENSE | 202 +++++++++++++++++++++++++++++
MANIFEST.in | 1 +
NOTICE | 13 ++
README.md | 126 +++++++++++++++++++
dictobj.egg-info/PKG-INFO | 15 +++
dictobj.egg-info/SOURCES.txt | 8 ++
dictobj.egg-info/dependency_links.txt | 1 +
dictobj.egg-info/top_level.txt | 1 +
dictobj.py | 230 ++++++++++++++++++++++++++++++++++
dictobj_test.py | 89 +++++++++++++
release.sh | 14 +++
setup.py | 32 +++++
14 files changed, 811 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..63443a1
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+*.pyc
+build/
+*.egg-info/
+dist/
+*.html
+*.zip
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..b1357ab
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,73 @@
+Changes
+=======
+
+v0.4
+----
+* Support for Python 3 added.
+* Removed the capabilities for <, >, <=, and >= on the
+ `DictionaryObject` to get Python 3 support properly added.
+
+v0.3.1
+------
+* Fixing the setup.py script so it works on older versions of Python.
+
+v0.3
+----
+* Added `DictionaryObject.asdict()` to return a copy of the internal
+ data as a `dict`, because some external libraries may require one
+ and won't accept a `DictionaryObject`.
+* Added another test to some older doctest code to make sure
+ `MutableDictionaryObject.__setitem__` works correctly.
+* Improved release.sh a little bit for generating pypi packages / docs.
+* Upgrading development status to "Production/Stable".
+
+v0.2.5
+------
+* Added `__setitem__` to `MutableDictionaryObject`.
+
+v0.2.4
+------
+* Properly formatted README.md and added some details about how
+to contribute to this project.
+* Added doctests to this project since the examples actually do
+work as valid tests taht can be run. Found some bugs, resolved
+them, and added everything to the automated testing suite.
+* Changed CHANGELOG to CHANGELOG.md.
+
+v0.2.3
+------
+* Adding the source code URL to the README so that it's easier
+for people to find and help contribute.
+
+v0.2.2
+------
+* Changelog started and prior versions added to this file.
+* Removed some code from `MutableDictionaryObject.__setattr__`
+that is no longer needed now that `DictionaryObject.__init__`
+properly handles initialization of `__dict__` when passed in a
+`DictionaryObject`.
+
+v0.2.1
+------
+* Improved the thoroughness of the documentation.
+* Added a description for PyPi.
+
+v0.2
+----
+* Fixed equality operators and the comparison method.
+* Fixed object copying when passing in another `DictionaryObject`.
+* Fixed handling of default values upon `__init__`.
+* Added `__setstate__` / `__getstate__` so pickle now works correctly
+with the classes.
+* Fixed error reporting on exceptions.
+* Improved `__repr__` so it gives a proper string represenation of
+our classes so they can later be eval'd.
+* Added more unit tests:
+ - `test_pickle`
+ - `test_copy`
+ - more equality tests
+* Added more examples to the documentation.
+
+v0.1.1
+------
+* First release.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..1aba38f
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1 @@
+include LICENSE
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..56d2f52
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,13 @@
+ Copyright 2012 "Grim Apps"
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..ac3393e
--- /dev/null
+++ b/README.md
@@ -0,0 +1,126 @@
+py-dictobj
+==========
+
+This package extends the functionality of the normal Python dictionary by affording the
+ability to lookup dictionary keys as instance attributes (i.e. `__getattr__`)
+instead of "indices" (i.e. `__getitem__`). Two caveats remain, however, prevent
+the use of `__getattr__` in certain circumstances. In these cases, access the
+`DictionaryObject` using `__getitem__` (e.g. `d['3x&']`). These cases are
+
+ 1. Names that do not follow the valid conventions for Normal Python syntax
+ 2. Names that match class attributes of the `DictionaryObject` class hierarchy
+ (e.g. `d.keys` will return the method, not the value, assuming `d['keys']` exists).
+
+There are two primary classes of interest: `DictionaryObject` and `MutableDictionaryObject`.
+`DictionaryObject` is the base class, and it acts as an immutable dictionary.
+`MutableDictionaryObject`, as the name implies, provides the ability to mutate the object via
+`__setattr__` (e.g. `d.x = 500`) and `__setitem__` (e.g. `d['x'] = 500`). For a description
+on the design considerations behind this choice, please see [Immutable-by-Default](#mutability).
+
+Care has been taken to make sure these classes are picklable so that they can be
+stored and passed around, especially in the case of multiprocessing. Care has
+also been taken that the `__repr__` of these classes can be eval()'d by the Python
+interpretter.
+
+Mutable vs Immutable
+--------------------
+
+The base `DictionaryObject` class is itself __immutable__, meaning that once the data is
+set during the call to `DictionaryObject.__init__`, no other keys may be added, nor
+may any existing keys have their values changed. One caveat to this is that if the
+values a `DictionaryObject` points to are themselves __mutable__, then the underlying
+object may change.
+
+If your use-case requires a more liberal `DictionaryObject` with _mutability_, please use
+`MutableDictionaryObject`. It behaves the same, but you can add keys via `__setattr__`
+or `__setitem__` (e.g. `d.x = 5` or `d['x'] = 5`).
+
+
+<a name="mutability"></a>
+
+Immutable-by-Default
+--------------------
+
+The base `DictionaryObject` was created as __immutable-by-default__ in order to facilitate
+[Separation of Concerns](http://en.wikipedia.org/wiki/Separation_of_concerns)
+By doing my best to ensure the top-level object is itself immutable, developers are more free
+to consider an object instance as _static values_. This allows them to make better assumptions,
+such as the fact they cannot change any values and indirectly interfere with the processing of the
+same data on another thread or process.
+
+In practice, Python itself does support a model of strong assurances with regard to immutability. So,
+the programmer must still be careful; however, this package should help.
+
+Installation
+------------
+
+If you have Python installed and wish to get the package directly from the
+[Python Package Index](http://pypi.python.org/pypi/dictobj), just run
+`pip install dictobj` from the command-line. If you already have a prior
+version installed, just run `pip install dictobj -U` instead.
+
+Contribute
+----------
+
+Please help contribute to this project by going to the
+[GitHub Project Repository](https://github.com/grimwm/py-dictobj) and doing one
+of a few things:
+
+ * send me pull requests through the github interface
+ * point me directly to your git repo so I can pull changes
+ * send bug reports and feature requests by filing them under the __Issues__ tab at the top
+
+Examples
+--------
+ >>> d = DictionaryObject({'a':1, 'b':True, 3:'x'})
+ >>> print d.a, d.b, d[3]
+ 1 True x
+
+ >>> d = DictionaryObject((('a',1),('b',2)))
+ >>> print d.a, d.b
+ 1 2
+
+ >>> d = DictionaryObject(a=1, b=True)
+ >>> print d
+ DictionaryObject({'a': 1, 'b': True})
+
+ >>> d = DictionaryObject({'a':1, 'b':True}, None)
+ >>> print d.a, d.b, d.c, d.d
+ 1 True None None
+
+ >>> d = DictionaryObject({'a':1}, None)
+ >>> m = MutableDictionaryObject(d)
+ >>> print d == m
+ True
+ >>> m.a = 0
+ >>> print d == m, d < m, d > m, d != m, d <= m, d >= m
+ False False True True False True
+
+ >>> import pickle
+ >>> m1 = MutableDictionaryObject({'a':1}, None)
+ >>> m2 = pickle.loads(pickle.dumps(m1))
+ >>> print m1 == m2
+ True
+ >>> m1.a = 3
+ >>> print m1 == m2
+ False
+
+ >>> d = DictionaryObject({'keys':[1,2], 'values':3, 'x':1})
+ >>> d.keys
+ <bound method DictionaryObject.keys of DictionaryObject({'keys': [1, 2], 'x': 1, 'values': 3})>
+ >>> d.values
+ <bound method DictionaryObject.values of DictionaryObject({'keys': [1, 2], 'x': 1, 'values': 3})>
+ >>> d.x
+ 1
+ >>> d['keys']
+ [1, 2]
+ >>> d['values']
+ 3
+
+ >>> import dictobj
+ >>> d = {'a':1, 'b':2}
+ >>> dictobj.DictionaryObject(d).asdict() == d
+ True
+ >>> d['c'] = {1:2, 3:4}
+ >>> dictobj.DictionaryObject(d).asdict() == d
+ True
diff --git a/dictobj.egg-info/PKG-INFO b/dictobj.egg-info/PKG-INFO
new file mode 100644
index 0000000..965ff47
--- /dev/null
+++ b/dictobj.egg-info/PKG-INFO
@@ -0,0 +1,15 @@
+Metadata-Version: 1.1
+Name: dictobj
+Version: 0.4
+Summary: A set of Python dictionary objects where keys can be accessed as instance attributes.
+Home-page: https://github.com/grimwm/py-dictobj
+Author: William Grim
+Author-email: william at grimapps.com
+License: UNKNOWN
+Description: UNKNOWN
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: License :: OSI Approved :: Apache Software License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
diff --git a/dictobj.egg-info/SOURCES.txt b/dictobj.egg-info/SOURCES.txt
new file mode 100644
index 0000000..28a5646
--- /dev/null
+++ b/dictobj.egg-info/SOURCES.txt
@@ -0,0 +1,8 @@
+LICENSE
+MANIFEST.in
+dictobj.py
+setup.py
+dictobj.egg-info/PKG-INFO
+dictobj.egg-info/SOURCES.txt
+dictobj.egg-info/dependency_links.txt
+dictobj.egg-info/top_level.txt
\ No newline at end of file
diff --git a/dictobj.egg-info/dependency_links.txt b/dictobj.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/dictobj.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/dictobj.egg-info/top_level.txt b/dictobj.egg-info/top_level.txt
new file mode 100644
index 0000000..d89ff63
--- /dev/null
+++ b/dictobj.egg-info/top_level.txt
@@ -0,0 +1 @@
+dictobj
diff --git a/dictobj.py b/dictobj.py
new file mode 100644
index 0000000..624ac46
--- /dev/null
+++ b/dictobj.py
@@ -0,0 +1,230 @@
+import pickle
+
+class DictionaryObject(object):
+ """
+ A class that has all the functionality of a normal Python dictionary, except
+ for the fact it is itself immutable. It also has the added feature of
+ being able to lookup values by using keys as attributes.
+
+ The reason for the class being immutable by default is to help make it a
+ little easier to use in multiprocessing situations. Granted, the underlying
+ values themselves are not deeply copied, but the aim is to enforce some
+ ensurances of immutability on the container class.
+
+ When using positional arguments, the first argument must always be something
+ that would be a valid argument for a dict(). However, a second, optional
+ argument may be passed to create a default value when keys are not found.
+
+ Examples:
+ >>> d = DictionaryObject({'a':1, 'b':True, 3:'x'})
+ >>> d.a == 1
+ True
+ >>> d.b
+ True
+ >>> d[3] == 'x'
+ True
+
+ >>> d = DictionaryObject((('a',1),('b',2)))
+ >>> d.a == 1
+ True
+ >>> d.b == 2
+ True
+
+ >>> d = DictionaryObject({'a':1, 'b':True}, None)
+ >>> d.a == 1
+ True
+ >>> d.b
+ True
+ >>> d.c
+
+ >>> d = DictionaryObject({'a':1}, None)
+ >>> m = MutableDictionaryObject(d)
+ >>> d == m
+ True
+ >>> m.a = 0
+ >>> d == m
+ False
+ >>> d != m
+ True
+
+ >>> import pickle
+ >>> m1 = MutableDictionaryObject({'a':1}, None)
+ >>> m2 = pickle.loads(pickle.dumps(m1))
+ >>> m1 == m2
+ True
+ >>> m1.a = 3
+ >>> m1 == m2
+ False
+ >>> m1.a == 3
+ True
+ >>> m1['c'] = 5
+ >>> m1['c']
+ 5
+ """
+ def __init__(self, contents=(), *args, **kwargs):
+ """
+ Take as input a dictionary-like object and return a DictionaryObject.
+ It also makes sure any keys containing dictionaries are also converted
+ to DictionaryObjects.
+ """
+ super(DictionaryObject, self).__init__()
+ if isinstance(contents, DictionaryObject):
+ self.__dict__.update(pickle.loads(pickle.dumps(contents.__dict__)))
+ return
+
+ self.__dict__['_items'] = dict(contents, **kwargs)
+
+ if len(args) > 1:
+ raise TypeError("too many arguments")
+
+ # If we have more than one argument passed in, use the second argument
+ # as a default value.
+ if args:
+ try:
+ default = type(self)(args[0])
+ except:
+ default = args[0]
+ self.__dict__['_defaultValue'] = default
+ else:
+ self.__dict__['_defaultValue'] = None
+ self.__dict__['_defaultIsSet'] = len(args) > 0
+
+ for k in self._items:
+ if isinstance(self._items[k], dict):
+ self._items[k] = type(self)(self._items[k])
+
+ def __setstate__(self, dict):
+ self.__dict__.update(dict)
+
+ def __getstate__(self):
+ return self.__dict__.copy()
+
+ def __getattr__(self, name):
+ """
+ This is the method that makes all the magic happen. Search for
+ 'name' in self._items and return the value if found. If a default
+ value has been set and 'name' is not found in self._items, return it.
+ Otherwise raise an AttributeError.
+
+ Example:
+ >>> d = DictionaryObject({'keys':[1,2], 'values':3, 'x':1})
+ >>> sorted(list(d.keys())) == ['keys', 'values', 'x']
+ True
+ >>> [1, 2] in list(d.values())
+ True
+ >>> 1 in list(d.values())
+ True
+ >>> d.x
+ 1
+ >>> d['keys']
+ [1, 2]
+ >>> d['values']
+ 3
+ """
+ if name in self._items:
+ return self._items[name]
+ if self._defaultIsSet:
+ return self._defaultValue
+ raise AttributeError("'%s' object has no attribute '%s'" % (type(self).__name__, name))
+
+ def __setattr__(self, name, value):
+ """
+ This class is immutable-by-default. See MutableDictionaryObject.
+ """
+ raise AttributeError("'%s' object does not support assignment" % type(self).__name__)
+
+ def __getitem__(self, name):
+ return self._items[name]
+
+ def __contains__(self, name):
+ return name in self._items
+
+ def __len__(self):
+ return len(self._items)
+
+ def __iter__(self):
+ return iter(self._items)
+
+ def __repr__(self):
+ if self._defaultIsSet:
+ params = "%s, %s" % (repr(self._items), self._defaultValue)
+ else:
+ params = repr(self._items)
+ return "%s(%s)" % (type(self).__name__, params)
+
+ def __cmp__(self, rhs):
+ if self < rhs:
+ return -1
+ if self > rhs:
+ return 1
+ return 0
+
+ def __eq__(self, rhs):
+ if self._items == rhs._items:
+ return self._defaultValue == rhs._defaultValue
+ return False
+
+ def __ne__(self, rhs):
+ return not (self == rhs)
+
+ def keys(self):
+ return self._items.keys()
+
+ def values(self):
+ return self._items.values()
+
+ def asdict(self):
+ """
+ Copy the data back out of here and into a dict. Then return it.
+ Some libraries may check specifically for dict objects, such
+ as the json library; so, this makes it convenient to get the data
+ back out.
+
+ >>> import dictobj
+ >>> d = {'a':1, 'b':2}
+ >>> dictobj.DictionaryObject(d).asdict() == d
+ True
+ >>> d['c'] = {1:2, 3:4}
+ >>> dictobj.DictionaryObject(d).asdict() == d
+ True
+ """
+ items = {}
+ for name in self._items:
+ value = self._items[name]
+ if isinstance(value, DictionaryObject):
+ items[name] = value.asdict()
+ else:
+ items[name] = value
+ return items
+
+class MutableDictionaryObject(DictionaryObject):
+ """
+ Slight enhancement of the DictionaryObject allowing one to add
+ attributes easily, in cases where that functionality is wanted.
+
+ Examples:
+ >>> d = MutableDictionaryObject({'a':1, 'b':True}, None)
+ >>> d.a == 1
+ True
+ >>> d.b == True
+ True
+ >>> d.c is None
+ True
+ >>> d.d is None
+ True
+ >>> d.c = 3
+ >>> del d.a
+ >>> d.a is None
+ True
+ >>> d.c == 3
+ True
+ """
+ def __setattr__(self, name, value):
+ self._items[name] = value
+
+ def __delattr__(self, name):
+ del self._items[name]
+
+ __setitem__ = __setattr__
+ __delitem__ = __delattr__
+
diff --git a/dictobj_test.py b/dictobj_test.py
new file mode 100644
index 0000000..9179143
--- /dev/null
+++ b/dictobj_test.py
@@ -0,0 +1,89 @@
+from dictobj import *
+
+import unittest
+import doctest
+
+class TestDictionaryObject(unittest.TestCase):
+ def setUp(self):
+ self.vanilla = DictionaryObject((('a',1),('b',2)))
+ self.kinky = DictionaryObject({'a':1, 'b':{'c':True, 'd':[1,2]}, 1:'x'})
+ self.default = DictionaryObject((), None, a=3)
+ self.mutable = MutableDictionaryObject(a=3, b=4)
+ self.mutableDefault = MutableDictionaryObject((), None, b=4)
+
+ def test_pickle(self):
+ default = pickle.loads(pickle.dumps(self.default))
+ self.assertEqual(default, self.default)
+
+ mutable = pickle.loads(pickle.dumps(self.mutable))
+ self.assertEqual(mutable, self.mutable)
+ mutable.a = 500
+ self.assertNotEqual(mutable, self.mutable)
+
+ def test_copy(self):
+ m = MutableDictionaryObject(self.default)
+ self.assertEqual(m, self.default)
+
+ m.a = 2
+ self.assertNotEqual(m, self.default)
+
+ def test_len(self):
+ self.assertEqual(3, len(self.kinky))
+
+ def test_default(self):
+ self.assertEqual(self.default.a, 3)
+ self.assertEqual(self.default.b, None)
+
+ self.assertEqual(self.mutableDefault.a, None)
+ self.assertEqual(self.mutableDefault.b, 4)
+
+ def test_mutable(self):
+ self.assertEqual(self.mutable.a, 3)
+ self.assertEqual(self.mutable.b, 4)
+
+ self.mutable.c = 5
+ self.assertEqual(self.mutable.c, 5)
+
+ self.assertRaises(AttributeError, getattr, self.mutable, 'd')
+
+ def test_mutable_default(self):
+ self.assertEqual(self.mutableDefault.b, 4)
+
+ self.mutableDefault.c = 5
+ self.assertEqual(self.mutableDefault.c, 5)
+
+ self.assertEqual(self.mutableDefault.a, None)
+
+ def test_iter(self):
+ keys = set([1,'a','b'])
+ for i,k in enumerate(self.kinky):
+ self.assertTrue(k in keys)
+
+ def test_getattr(self):
+ self.assertEqual(self.kinky.a, 1)
+ self.assertEqual(self.kinky.b, DictionaryObject({'c':True, 'd':[1,2]}))
+
+ def test_getitem(self):
+ self.assertEqual(self.kinky['a'], 1)
+ self.assertEqual(self.kinky['b'], DictionaryObject({'c':True, 'd':[1,2]}))
+ self.assertEqual(self.kinky[1], 'x')
+
+ def test_exception(self):
+ self.assertRaises(AttributeError, setattr, self.kinky, 'c', 3)
+
+ def test_setitem(self):
+ self.mutable.x = 500
+ self.assertEqual(self.mutable.x, 500)
+
+ self.mutable['y'] = 100
+ self.assertEqual(self.mutable.y, 100)
+
+def load_tests(loader, tests, pattern):
+ import dictobj
+ suite = unittest.TestSuite()
+ suite.addTests(tests)
+ suite.addTest(doctest.DocTestSuite(dictobj))
+ return suite
+
+if '__main__' == __name__:
+ unittest.main()
diff --git a/release.sh b/release.sh
new file mode 100755
index 0000000..eccdfc7
--- /dev/null
+++ b/release.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+# Run build
+pandoc README.md -w rst -o README.txt
+python setup.py sdist upload
+markdown README.md > index.html
+zip pypi.zip index.html
+
+# Do cleanup
+rm -f README.txt index.html
+rm -rf dist
+rm -rf dictobj.egg-info
+rm -rf __pycache__
+rm -f *.pyc
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..48f5bab
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,32 @@
+from setuptools import setup
+import os
+
+def read(filename):
+ fin = None
+ data = None
+ try:
+ fin = open(filename)
+ data = fin.read()
+ finally:
+ if fin is not None:
+ fin.close()
+ return data
+
+setup(
+ name='dictobj',
+ version='0.4',
+ author='William Grim',
+ author_email='william at grimapps.com',
+ url='https://github.com/grimwm/py-dictobj',
+ classifiers = [
+ 'Development Status :: 5 - Production/Stable',
+ 'License :: OSI Approved :: Apache Software License',
+ 'Operating System :: OS Independent',
+ 'Programming Language :: Python',
+ 'Topic :: Software Development :: Libraries :: Python Modules',
+ ],
+ description='A set of Python dictionary objects where keys can be accessed as instance attributes.',
+ long_description=read('README.txt') if os.path.exists('README.txt') else '',
+ py_modules=['dictobj'],
+ test_suite='dictobj_test',
+ )
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/python-dictobj.git
More information about the debian-med-commit
mailing list