diff options
author | Matthew Peveler <matt.peveler@gmail.com> | 2021-01-30 05:09:50 +0000 |
---|---|---|
committer | Matthew Peveler <matt.peveler@gmail.com> | 2021-01-30 05:12:55 +0000 |
commit | dc7c3f45a8f4daecb687f964120a9d9174257e60 (patch) | |
tree | bf6ab276931efb4e1b8ced0d35484b6d58357750 | |
parent | c939a02cb497244bac966a3e7513ee6007885c64 (diff) | |
download | asciidoc-py3-dc7c3f45a8f4daecb687f964120a9d9174257e60.tar.gz |
move Dict classes into own module
Signed-off-by: Matthew Peveler <matt.peveler@gmail.com>
-rw-r--r-- | .github/workflows/test.yml | 7 | ||||
-rw-r--r-- | .gitignore | 5 | ||||
-rw-r--r-- | asciidoc/a2x.py | 42 | ||||
-rw-r--r-- | asciidoc/asciidoc.py | 59 | ||||
-rw-r--r-- | asciidoc/collections.py | 69 | ||||
-rw-r--r-- | tests/test_collections.py | 37 |
6 files changed, 120 insertions, 99 deletions
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index da94989..a3d3930 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -64,7 +64,10 @@ jobs: texlive-latex-base \ xsltproc - - run: python asciidoc/asciidoc.py --doctest + - run: pip install -U pytest + + - run: python -m asciidoc.asciidoc --doctest + - run: python -m pytest - run: time python tests/testasciidoc.py run - run: git clean -x -f doc tests/data @@ -94,5 +97,5 @@ jobs: with: python-version: ${{ env.python-version }} - - run: python asciidoc/asciidoc.py --doctest + - run: python -m asciidoc.asciidoc --doctest - run: python tests/testasciidoc.py run --number 6 @@ -27,3 +27,8 @@ MANIFEST build/ dist/ *.egg-info/ + +# test artifacts +.coverage +htmlcov/ +.pytest_cache/ diff --git a/asciidoc/a2x.py b/asciidoc/a2x.py index d55eeda..4a23293 100644 --- a/asciidoc/a2x.py +++ b/asciidoc/a2x.py @@ -42,10 +42,8 @@ import zipfile import xml.dom.minidom import mimetypes -try: - from . import asciidoc -except ImportError: - import asciidoc +from . import asciidoc +from .collections import DefaultAttrDict as AttrDict CONF_DIR = os.path.join(os.path.dirname(__file__), 'resources') METADATA = {} @@ -134,42 +132,6 @@ def flatten(array): return ret -class AttrDict(dict): - """ - Like a dictionary except values can be accessed as attributes i.e. obj.foo - can be used in addition to obj['foo']. - If self._default has been set then it will be returned if a non-existent - attribute is accessed (instead of raising an AttributeError). - """ - def __getattr__(self, key): - try: - return self[key] - except KeyError as k: - if '_default' in self: - return self['_default'] - else: - raise AttributeError from k - - def __setattr__(self, key, value): - self[key] = value - - def __delattr__(self, key): - try: - del self[key] - except KeyError as k: - raise AttributeError from k - - def __repr__(self): - return '<AttrDict ' + dict.__repr__(self) + '>' - - def __getstate__(self): - return dict(self) - - def __setstate__(self, value): - for k, v in value.items(): - self[k] = v - - def isexecutable(file_name): return os.path.isfile(file_name) and os.access(file_name, os.X_OK) diff --git a/asciidoc/asciidoc.py b/asciidoc/asciidoc.py index 7ff72b0..b26a4c6 100644 --- a/asciidoc/asciidoc.py +++ b/asciidoc/asciidoc.py @@ -32,6 +32,8 @@ import zipfile from ast import literal_eval from collections import OrderedDict +from .collections import AttrDict, InsensitiveDict + CONF_DIR = os.path.join(os.path.dirname(__file__), 'resources') METADATA = {} with open(os.path.join(os.path.dirname(__file__), '__metadata__.py')) as f: @@ -69,63 +71,6 @@ class EAsciiDoc(Exception): pass -class AttrDict(dict): - """ - Like a dictionary except values can be accessed as attributes i.e. obj.foo - can be used in addition to obj['foo']. - If an item is not present None is returned. - """ - def __getattr__(self, key): - try: - return self[key] - except KeyError: - return None - - def __setattr__(self, key, value): - self[key] = value - - def __delattr__(self, key): - try: - del self[key] - except KeyError as k: - raise AttributeError(k) - - def __repr__(self): - return '<AttrDict ' + dict.__repr__(self) + '>' - - def __getstate__(self): - return dict(self) - - def __setstate__(self, value): - for k, v in list(value.items()): - self[k] = v - - -class InsensitiveDict(dict): - """ - Like a dictionary except key access is case insensitive. - Keys are stored in lower case. - """ - def __getitem__(self, key): - return dict.__getitem__(self, key.lower()) - - def __setitem__(self, key, value): - dict.__setitem__(self, key.lower(), value) - - def has_key(self, key): - return key.lower() in self - - def get(self, key, default=None): - return dict.get(self, key.lower(), default) - - def update(self, dict): - for k, v in list(dict.items()): - self[k] = v - - def setdefault(self, key, default=None): - return dict.setdefault(self, key.lower(), default) - - class Trace(object): """ Used in conjunction with the 'trace' attribute to generate diagnostic diff --git a/asciidoc/collections.py b/asciidoc/collections.py new file mode 100644 index 0000000..36cfded --- /dev/null +++ b/asciidoc/collections.py @@ -0,0 +1,69 @@ +class AttrDict(dict): + """ + Like a dictionary except values can be accessed as attributes i.e. obj.foo + can be used in addition to obj['foo']. + If an item is not present None is returned. + """ + def __getattr__(self, key): + try: + return self[key] + except KeyError: + return None + + def __setattr__(self, key, value): + self[key] = value + + def __delattr__(self, key): + try: + del self[key] + except KeyError as k: + raise AttributeError(k) + + def __repr__(self): + return '<AttrDict ' + dict.__repr__(self) + '>' + + def __getstate__(self): + return dict(self) + + def __setstate__(self, value): + for k, v in value.items(): + self[k] = v + + +class DefaultAttrDict(AttrDict): + def __getattr__(self, key): + try: + return self[key] + except KeyError as k: + if '_default' in self: + return self['_default'] + else: + raise AttributeError from k + + +class InsensitiveDict(dict): + """ + Like a dictionary except key access is case insensitive. + Keys are stored in lower case. + """ + def __getitem__(self, key): + return dict.__getitem__(self, key.lower()) + + def __setitem__(self, key, value): + dict.__setitem__(self, key.lower(), value) + + def __delitem__(self, key): + dict.__delitem__(self, key.lower()) + + def get(self, key, default=None): + return dict.get(self, key.lower(), default) + + def update(self, dict): + for k, v in dict.items(): + self[k] = v + + def setdefault(self, key, default=None): + return dict.setdefault(self, key.lower(), default) + + def __contains__(self, key): + return key.lower() in dict(self) diff --git a/tests/test_collections.py b/tests/test_collections.py new file mode 100644 index 0000000..5007948 --- /dev/null +++ b/tests/test_collections.py @@ -0,0 +1,37 @@ +import pytest +from asciidoc.collections import AttrDict, DefaultAttrDict, InsensitiveDict + + +def test_attr_dict(): + d = AttrDict() + d.a = 1 + d['b'] = 2 + assert d['a'] == 1 + assert d.b == 2 + del d['a'] + del d.b + assert 'a' not in d + assert 'b' not in d + assert d.c is None + + with pytest.raises(AttributeError): + del d.c + + +def test_default_attr_dict(): + d = DefaultAttrDict() + with pytest.raises(AttributeError): + d.a + d._default = 'test' + + assert d.a == 'test' + + +def test_insensitive_dict(): + d = InsensitiveDict() + d['A'] = 1 + assert d['a'] == 1 + d['aBaBa'] = 2 + assert 'AbAbA' in d + del d['abaBA'] + assert ('ababa' in d) is False |