Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-lexicon for openSUSE:Factory 
checked in at 2021-09-21 21:12:24
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-lexicon (Old)
 and      /work/SRC/openSUSE:Factory/.python-lexicon.new.1899 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-lexicon"

Tue Sep 21 21:12:24 2021 rev:4 rq:919616 version:2.0.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-lexicon/python-lexicon.changes    
2018-12-24 11:39:14.217566842 +0100
+++ /work/SRC/openSUSE:Factory/.python-lexicon.new.1899/python-lexicon.changes  
2021-09-21 21:13:04.738634314 +0200
@@ -1,0 +2,17 @@
+Fri Sep 17 03:43:56 UTC 2021 - Steve Kowalik <steven.kowa...@suse.com>
+
+- Update to 2.0.1:
+  * Fix up some project metadata.
+  * Dropped support for Python <3.6
+  * Added a _version submodule and imported its dunder-attributes into
+    the top level module
+  * Migrated CI to CircleCI (from Travis)
+  * Migrated tests to pytest(-relaxed)
+  * Moved changelog to stub Sphinx project for Releases plugin
+  * Changed README to ReStructured Text (from Markdown) 
+- Dropped patch add_test_init.patch, no longer required.
+- Added patch add-pytest-ini.patch:
+  * Add the pytest.ini file that isn't included in the source distribution
+    so the testsuite works.
+
+-------------------------------------------------------------------

Old:
----
  add_test_init.patch
  lexicon-1.0.0.tar.gz

New:
----
  add-pytest-ini.patch
  lexicon-2.0.1.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-lexicon.spec ++++++
--- /var/tmp/diff_new_pack.GVA39X/_old  2021-09-21 21:13:05.326634979 +0200
+++ /var/tmp/diff_new_pack.GVA39X/_new  2021-09-21 21:13:05.330634984 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package python-lexicon
 #
-# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2021 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -18,15 +18,13 @@
 
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 Name:           python-lexicon
-Version:        1.0.0
+Version:        2.0.1
 Release:        0
 Summary:        Python dict subclass(es) with aliasing and attribute access
 License:        BSD-2-Clause
-Group:          Development/Languages/Python
 URL:            https://github.com/bitprophet/lexicon
 Source:         
https://files.pythonhosted.org/packages/source/l/lexicon/lexicon-%{version}.tar.gz
-# PATCH-FIX-UPSTREAM: add_test_init.patch # fix execution of tests
-Patch0:         
https://github.com/bitprophet/lexicon/pull/10.patch#/add_test_init.patch
+Patch0:         add-pytest-ini.patch
 BuildRequires:  %{python_module setuptools}
 BuildRequires:  fdupes
 BuildRequires:  python-rpm-macros
@@ -35,8 +33,7 @@
 Conflicts:      python-dns-lexicon
 BuildArch:      noarch
 # SECTION tests
-BuildRequires:  %{python_module six}
-BuildRequires:  %{python_module spec}
+BuildRequires:  %{python_module pytest-relaxed}
 # /SECTION tests
 %python_subpackages
 
@@ -49,7 +46,7 @@
 
 %prep
 %setup -q -n lexicon-%{version}
-%patch0 -p1
+%autopatch -p1
 
 %build
 %python_build
@@ -59,10 +56,10 @@
 %python_expand %fdupes %{buildroot}%{$python_sitelib}
 
 %check
-%python_expand spec-%{$python_bin_suffix}
+%pytest
 
 %files %{python_files}
-%doc README.md
+%doc README.rst
 %license LICENSE
 %{python_sitelib}/*
 

++++++ add-pytest-ini.patch ++++++
Index: lexicon-2.0.1/pytest.ini
===================================================================
--- /dev/null
+++ lexicon-2.0.1/pytest.ini
@@ -0,0 +1,3 @@
+[pytest]
+testpaths = tests
+python_files = *
++++++ lexicon-1.0.0.tar.gz -> lexicon-2.0.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lexicon-1.0.0/LICENSE new/lexicon-2.0.1/LICENSE
--- old/lexicon-1.0.0/LICENSE   2016-01-01 21:26:33.000000000 +0100
+++ new/lexicon-2.0.1/LICENSE   2020-01-11 00:51:19.000000000 +0100
@@ -1,4 +1,4 @@
-Copyright (c) 2016 Jeff Forcier.
+Copyright (c) 2020 Jeff Forcier.
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lexicon-1.0.0/MANIFEST.in 
new/lexicon-2.0.1/MANIFEST.in
--- old/lexicon-1.0.0/MANIFEST.in       2014-05-02 00:57:22.000000000 +0200
+++ new/lexicon-2.0.1/MANIFEST.in       2021-07-09 00:28:33.000000000 +0200
@@ -1,5 +1,6 @@
 include LICENSE
-include README.md
+include README.rst
+recursive-include docs *.rst *.py
 include dev-requirements.txt
 recursive-include tests *
 recursive-exclude tests *.pyc *.pyo
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lexicon-1.0.0/PKG-INFO new/lexicon-2.0.1/PKG-INFO
--- old/lexicon-1.0.0/PKG-INFO  2016-02-17 23:10:28.000000000 +0100
+++ new/lexicon-2.0.1/PKG-INFO  2021-09-10 20:38:28.000000000 +0200
@@ -1,96 +1,127 @@
-Metadata-Version: 1.1
+Metadata-Version: 2.1
 Name: lexicon
-Version: 1.0.0
+Version: 2.0.1
 Summary: Powerful dict subclass(es) with aliasing & attribute access
-Home-page: https://github.com/bitprophet/lexicon
+Home-page: https://github.com/bitprophet/lexicon#what
 Author: Jeff Forcier
 Author-email: j...@bitprophet.org
 License: BSD
-Description: ## WHAT
-        
-        Lexicon is a simple Python 2.6+ and 3.3+ compatible collection of 
`dict`
-        subclasses providing extra power:
-        
-        * `AliasDict`, a dictionary supporting both simple and complex key 
aliasing:
-            * Alias a single key to another key, so that e.g. `mydict['bar']` 
points to
-            `mydict['foo']`, for both reads and writes.
-            * Alias a single key to a list of other keys, for writing only, 
e.g. with
-            `active_groups = AliasDict({'ops': True, 'biz': True, 'dev': True,
-            'product': True})` one can make an alias `'tech'` mapping to 
`('ops',
-            'dev')` and then e.g. `active_groups['tech'] = False`.
-            * Aliasing is recursive: an alias pointing to another alias will 
behave as
-            if it points to the other alias' target.
-        * `AttributeDict`, supporting attribute read & write access, e.g.
-          `mydict = AttributeDict({'foo': 'bar'})` exhibits `mydict.foo` and
-          `mydict.foo = 'new value'`.
-        * `Lexicon`, a subclass of both of the above which exhibits both sets 
of
+Project-URL: Source, https://github.com/bitprophet/lexicon
+Project-URL: Changelog, 
https://github.com/bitprophet/lexicon/blob/main/docs/changelog.rst
+Project-URL: CI, https://app.circleci.com/pipelines/github/bitprophet/lexicon
+Description: |version| |python| |license| |ci| |coverage|
+        
+        .. |version| image:: https://img.shields.io/pypi/v/lexicon
+            :target: https://pypi.org/project/lexicon/
+            :alt: PyPI - Package Version
+        .. |python| image:: https://img.shields.io/pypi/pyversions/lexicon
+            :target: https://pypi.org/project/lexicon/
+            :alt: PyPI - Python Version
+        .. |license| image:: https://img.shields.io/pypi/l/lexicon
+            :target: https://github.com/bitprophet/lexicon/blob/main/LICENSE
+            :alt: PyPI - License
+        .. |ci| image:: 
https://img.shields.io/circleci/build/github/bitprophet/lexicon/main
+            :target: 
https://app.circleci.com/pipelines/github/bitprophet/lexicon
+            :alt: CircleCI
+        .. |coverage| image:: 
https://img.shields.io/codecov/c/gh/bitprophet/lexicon
+            :target: https://app.codecov.io/gh/bitprophet/lexicon
+            :alt: Codecov
+        
+        WHAT
+        ====
+        
+        Lexicon is a simple collection of Python ``dict`` subclasses providing 
extra
+        power:
+        
+        - ``AliasDict``, a dictionary supporting both simple and complex key 
aliasing:
+        
+            - Alias a single key to another key, so that e.g. 
``mydict['bar']`` points
+              to ``mydict['foo']``, for both reads and writes.
+            - Alias a single key to a list of other keys, for writing only, 
e.g. with
+              ``active_groups = AliasDict({'ops': True, 'biz': True, 'dev': 
True,
+              'product': True})`` one can make an alias ``'tech'`` mapping to 
``('ops',
+              'dev')`` and then e.g. ``active_groups['tech'] = False``.
+            - Aliasing is recursive: an alias pointing to another alias will 
behave as
+              if it points to the other alias' target.
+        
+        - ``AttributeDict``, supporting attribute read & write access, e.g. 
``mydict =
+          AttributeDict({'foo': 'bar'})`` exhibits ``mydict.foo`` and 
``mydict.foo =
+          'new value'``.
+        - ``Lexicon``, a subclass of both of the above which exhibits both 
sets of
           behavior.
         
-        ## HOW
+        HOW
+        ===
         
-        * `pip install lexicon`
-        * `from lexicon import Lexicon` (or one of the superclasses)
-        * Use as needed.
-        
-        You can install the [development
-        
version](https://github.com/bitprophet/lexicon/tarball/master#egg=lexicon-dev)
-        via `pip install lexicon==dev`.
+        - ``pip install lexicon``
+        - ``from lexicon import Lexicon`` (or one of the superclasses)
+        - Use as needed.
         
         If you have a clone of the source repository, you can run the tests 
like so:
         
-        * `pip install -r dev-requirements.txt`
-        * `spec`
+        - ``pip install -r dev-requirements.txt``
+        - ``inv test``
         
-        ## API
+        API
+        ===
         
-        ### `AliasDict`
+        ``AliasDict``
+        -------------
         
-        In all examples, `'myalias'` is the alias and `'realkey'` is the 
"real",
+        In all examples, ``'myalias'`` is the alias and ``'realkey'`` is the 
"real",
         unaliased key.
         
-        * `alias(from_'myalias', to='realkey')`: Alias `myalias` to `realkey` 
so
-          `d['myalias']` behaves exactly like `d['realkey']` for both reads 
and writes.
-            * `from_` is the first keyword argument, but typically it can be 
omitted
-            and still reads fine. See below examples for this usage.
-          See below for details on how an alias affects other dict operations.
-        * `alias('myalias', to=('realkey', 'otherrealkey'))`: Alias `myalias` 
to
-          both `realkey` and `otherrealkey`. As you might expect, this only 
works well
-          for writes, as there is never any guarantee that all targets of the 
alias
-          will contain the same value.
-        * `unalias('myalias')`: Removes the `myalias` alias; any subsequent
-          reads/writes to `myalias` will behave as normal for a regular `dict`.
-        * `'myalias' in d` (aka `__contains__`): Returns True when given an 
alias, so
-          if `myalias` is an alias to some other key, dictionary membership 
tests will
-          behave as if `myalias` is set.
-        * `del d['myalias']` (aka `__delitem__`): This effectively becomes `del
-          d['realkey']` -- to remove the alias itself, use `unalias()`.
-        * `del d['realkey']`: Deletes the real key/value pair (i.e. it calls
-          `dict.__del__`) but doesn't touch any aliases pointing to `realkey`.
-            * As a result, "dangling" aliases pointing to nonexistent keys 
will raise
-            `KeyError` on access, but will continue working if the target key 
is
-            repopulated later.
+        - ``alias(from_'myalias', to='realkey')``: Alias ``myalias`` to 
``realkey`` so
+          ``d['myalias']`` behaves exactly like ``d['realkey']`` for both 
reads and
+          writes.
+          
+            - ``from_`` is the first keyword argument, but typically it can be 
omitted
+              and still reads fine. See below examples for this usage. See 
below for
+              details on how an alias affects other dict operations.
+        
+        - ``alias('myalias', to=('realkey', 'otherrealkey'))``: Alias 
``myalias`` to
+          both ``realkey`` and ``otherrealkey``. As you might expect, this 
only works
+          well for writes, as there is never any guarantee that all targets of 
the
+          alias will contain the same value.
+        - ``unalias('myalias')``: Removes the ``myalias`` alias; any subsequent
+          reads/writes to ``myalias`` will behave as normal for a regular 
``dict``.
+        - ``'myalias' in d`` (aka ``__contains__``): Returns True when given 
an alias,
+          so if ``myalias`` is an alias to some other key, dictionary 
membership tests
+          will behave as if ``myalias`` is set.
+        - ``del d['myalias']`` (aka ``__delitem__``): This effectively becomes 
``del
+          d['realkey']`` -- to remove the alias itself, use ``unalias()``.
+        - ``del d['realkey']``: Deletes the real key/value pair (i.e. it calls
+          ``dict.__del__``) but doesn't touch any aliases pointing to 
``realkey``.
+        
+            - As a result, "dangling" aliases pointing to nonexistent keys 
will raise
+              ``KeyError`` on access, but will continue working if the target 
key is
+              repopulated later.
         
         Caveats:
         
-        * Because of the single-key/multi-key duality, `AliasDict` is 
incapable of
-          honoring non-string-type keys when aliasing (it must test 
`isinstance(key,
-          basestring)` to tell strings apart from non-string iterables).
-            * `AliasDict` instances may still *use* non-string keys, of course 
-- it
-            just can't use them as alias targets.
-        
-        ### `AttributeDict`
-        
-        * `d.key = 'value'` (aka `__setattr__`): Maps directly to `d['key'] = 
'value'`.
-        * `d.key` (aka `__getattr__`): Maps directly to `d['key']`.
-        * `del d.key` (aka `__delattr__`): Maps directly to `del d['key']`.
-        * Collisions between "real" or pre-existing attributes, and
+        - Because of the single-key/multi-key duality, ``AliasDict`` is 
incapable of
+          honoring non-string-type keys when aliasing (it must test 
``isinstance(key,
+          basestring)`` to tell strings apart from non-string iterables).
+        
+            - ``AliasDict`` instances may still *use* non-string keys, of 
course -- it
+              just can't use them as alias targets.
+        
+        ``AttributeDict``
+        -----------------
+        
+        - ``d.key = 'value'`` (aka ``__setattr__``): Maps directly to 
``d['key'] =
+          'value'``.
+        - ``d.key`` (aka ``__getattr__``): Maps directly to ``d['key']``.
+        - ``del d.key`` (aka ``__delattr__``): Maps directly to ``del 
d['key']``.
+        - Collisions between "real" or pre-existing attributes, and
           attributes-as-dict-keys, always results in the real attribute 
winning. Thus
-          it isn't possible to use attribute access to access e.g. `d['get']`.
+          it isn't possible to use attribute access to access e.g. 
``d['get']``.
         
-        ### `Lexicon`
+        ``Lexicon``
+        -----------
         
-        Lexicon subclasses from `AttributeDict` first, then `AliasDict`, with 
the end
-        result that attribute access will honor aliases. E.g.:
+        Lexicon subclasses from ``AttributeDict`` first, then ``AliasDict``, 
with the
+        end result that attribute access will honor aliases. E.g.:
         
             d = Lexicon()
             d.alias('myalias', to='realkey')
@@ -106,13 +137,12 @@
 Classifier: Operating System :: Unix
 Classifier: Operating System :: POSIX
 Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.6
-Classifier: Programming Language :: Python :: 2.7
 Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.3
-Classifier: Programming Language :: Python :: 3.4
-Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
 Classifier: Topic :: Software Development
 Classifier: Topic :: Software Development :: Libraries
 Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Description-Content-Type: text/x-rst
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lexicon-1.0.0/README.md new/lexicon-2.0.1/README.md
--- old/lexicon-1.0.0/README.md 2014-05-02 00:57:22.000000000 +0200
+++ new/lexicon-2.0.1/README.md 1970-01-01 01:00:00.000000000 +0100
@@ -1,90 +0,0 @@
-## WHAT
-
-Lexicon is a simple Python 2.6+ and 3.3+ compatible collection of `dict`
-subclasses providing extra power:
-
-* `AliasDict`, a dictionary supporting both simple and complex key aliasing:
-    * Alias a single key to another key, so that e.g. `mydict['bar']` points to
-    `mydict['foo']`, for both reads and writes.
-    * Alias a single key to a list of other keys, for writing only, e.g. with
-    `active_groups = AliasDict({'ops': True, 'biz': True, 'dev': True,
-    'product': True})` one can make an alias `'tech'` mapping to `('ops',
-    'dev')` and then e.g. `active_groups['tech'] = False`.
-    * Aliasing is recursive: an alias pointing to another alias will behave as
-    if it points to the other alias' target.
-* `AttributeDict`, supporting attribute read & write access, e.g.
-  `mydict = AttributeDict({'foo': 'bar'})` exhibits `mydict.foo` and
-  `mydict.foo = 'new value'`.
-* `Lexicon`, a subclass of both of the above which exhibits both sets of
-  behavior.
-
-## HOW
-
-* `pip install lexicon`
-* `from lexicon import Lexicon` (or one of the superclasses)
-* Use as needed.
-
-You can install the [development
-version](https://github.com/bitprophet/lexicon/tarball/master#egg=lexicon-dev)
-via `pip install lexicon==dev`.
-
-If you have a clone of the source repository, you can run the tests like so:
-
-* `pip install -r dev-requirements.txt`
-* `spec`
-
-## API
-
-### `AliasDict`
-
-In all examples, `'myalias'` is the alias and `'realkey'` is the "real",
-unaliased key.
-
-* `alias(from_'myalias', to='realkey')`: Alias `myalias` to `realkey` so
-  `d['myalias']` behaves exactly like `d['realkey']` for both reads and writes.
-    * `from_` is the first keyword argument, but typically it can be omitted
-    and still reads fine. See below examples for this usage.
-  See below for details on how an alias affects other dict operations.
-* `alias('myalias', to=('realkey', 'otherrealkey'))`: Alias `myalias` to
-  both `realkey` and `otherrealkey`. As you might expect, this only works well
-  for writes, as there is never any guarantee that all targets of the alias
-  will contain the same value.
-* `unalias('myalias')`: Removes the `myalias` alias; any subsequent
-  reads/writes to `myalias` will behave as normal for a regular `dict`.
-* `'myalias' in d` (aka `__contains__`): Returns True when given an alias, so
-  if `myalias` is an alias to some other key, dictionary membership tests will
-  behave as if `myalias` is set.
-* `del d['myalias']` (aka `__delitem__`): This effectively becomes `del
-  d['realkey']` -- to remove the alias itself, use `unalias()`.
-* `del d['realkey']`: Deletes the real key/value pair (i.e. it calls
-  `dict.__del__`) but doesn't touch any aliases pointing to `realkey`.
-    * As a result, "dangling" aliases pointing to nonexistent keys will raise
-    `KeyError` on access, but will continue working if the target key is
-    repopulated later.
-
-Caveats:
-
-* Because of the single-key/multi-key duality, `AliasDict` is incapable of
-  honoring non-string-type keys when aliasing (it must test `isinstance(key,
-  basestring)` to tell strings apart from non-string iterables).
-    * `AliasDict` instances may still *use* non-string keys, of course -- it
-    just can't use them as alias targets.
-
-### `AttributeDict`
-
-* `d.key = 'value'` (aka `__setattr__`): Maps directly to `d['key'] = 'value'`.
-* `d.key` (aka `__getattr__`): Maps directly to `d['key']`.
-* `del d.key` (aka `__delattr__`): Maps directly to `del d['key']`.
-* Collisions between "real" or pre-existing attributes, and
-  attributes-as-dict-keys, always results in the real attribute winning. Thus
-  it isn't possible to use attribute access to access e.g. `d['get']`.
-
-### `Lexicon`
-
-Lexicon subclasses from `AttributeDict` first, then `AliasDict`, with the end
-result that attribute access will honor aliases. E.g.:
-
-    d = Lexicon()
-    d.alias('myalias', to='realkey')
-    d.myalias = 'foo'
-    print d.realkey # prints 'foo'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lexicon-1.0.0/README.rst new/lexicon-2.0.1/README.rst
--- old/lexicon-1.0.0/README.rst        1970-01-01 01:00:00.000000000 +0100
+++ new/lexicon-2.0.1/README.rst        2021-09-04 17:44:12.000000000 +0200
@@ -0,0 +1,118 @@
+|version| |python| |license| |ci| |coverage|
+
+.. |version| image:: https://img.shields.io/pypi/v/lexicon
+    :target: https://pypi.org/project/lexicon/
+    :alt: PyPI - Package Version
+.. |python| image:: https://img.shields.io/pypi/pyversions/lexicon
+    :target: https://pypi.org/project/lexicon/
+    :alt: PyPI - Python Version
+.. |license| image:: https://img.shields.io/pypi/l/lexicon
+    :target: https://github.com/bitprophet/lexicon/blob/main/LICENSE
+    :alt: PyPI - License
+.. |ci| image:: 
https://img.shields.io/circleci/build/github/bitprophet/lexicon/main
+    :target: https://app.circleci.com/pipelines/github/bitprophet/lexicon
+    :alt: CircleCI
+.. |coverage| image:: https://img.shields.io/codecov/c/gh/bitprophet/lexicon
+    :target: https://app.codecov.io/gh/bitprophet/lexicon
+    :alt: Codecov
+
+WHAT
+====
+
+Lexicon is a simple collection of Python ``dict`` subclasses providing extra
+power:
+
+- ``AliasDict``, a dictionary supporting both simple and complex key aliasing:
+
+    - Alias a single key to another key, so that e.g. ``mydict['bar']`` points
+      to ``mydict['foo']``, for both reads and writes.
+    - Alias a single key to a list of other keys, for writing only, e.g. with
+      ``active_groups = AliasDict({'ops': True, 'biz': True, 'dev': True,
+      'product': True})`` one can make an alias ``'tech'`` mapping to ``('ops',
+      'dev')`` and then e.g. ``active_groups['tech'] = False``.
+    - Aliasing is recursive: an alias pointing to another alias will behave as
+      if it points to the other alias' target.
+
+- ``AttributeDict``, supporting attribute read & write access, e.g. ``mydict =
+  AttributeDict({'foo': 'bar'})`` exhibits ``mydict.foo`` and ``mydict.foo =
+  'new value'``.
+- ``Lexicon``, a subclass of both of the above which exhibits both sets of
+  behavior.
+
+HOW
+===
+
+- ``pip install lexicon``
+- ``from lexicon import Lexicon`` (or one of the superclasses)
+- Use as needed.
+
+If you have a clone of the source repository, you can run the tests like so:
+
+- ``pip install -r dev-requirements.txt``
+- ``inv test``
+
+API
+===
+
+``AliasDict``
+-------------
+
+In all examples, ``'myalias'`` is the alias and ``'realkey'`` is the "real",
+unaliased key.
+
+- ``alias(from_'myalias', to='realkey')``: Alias ``myalias`` to ``realkey`` so
+  ``d['myalias']`` behaves exactly like ``d['realkey']`` for both reads and
+  writes.
+  
+    - ``from_`` is the first keyword argument, but typically it can be omitted
+      and still reads fine. See below examples for this usage. See below for
+      details on how an alias affects other dict operations.
+
+- ``alias('myalias', to=('realkey', 'otherrealkey'))``: Alias ``myalias`` to
+  both ``realkey`` and ``otherrealkey``. As you might expect, this only works
+  well for writes, as there is never any guarantee that all targets of the
+  alias will contain the same value.
+- ``unalias('myalias')``: Removes the ``myalias`` alias; any subsequent
+  reads/writes to ``myalias`` will behave as normal for a regular ``dict``.
+- ``'myalias' in d`` (aka ``__contains__``): Returns True when given an alias,
+  so if ``myalias`` is an alias to some other key, dictionary membership tests
+  will behave as if ``myalias`` is set.
+- ``del d['myalias']`` (aka ``__delitem__``): This effectively becomes ``del
+  d['realkey']`` -- to remove the alias itself, use ``unalias()``.
+- ``del d['realkey']``: Deletes the real key/value pair (i.e. it calls
+  ``dict.__del__``) but doesn't touch any aliases pointing to ``realkey``.
+
+    - As a result, "dangling" aliases pointing to nonexistent keys will raise
+      ``KeyError`` on access, but will continue working if the target key is
+      repopulated later.
+
+Caveats:
+
+- Because of the single-key/multi-key duality, ``AliasDict`` is incapable of
+  honoring non-string-type keys when aliasing (it must test ``isinstance(key,
+  basestring)`` to tell strings apart from non-string iterables).
+
+    - ``AliasDict`` instances may still *use* non-string keys, of course -- it
+      just can't use them as alias targets.
+
+``AttributeDict``
+-----------------
+
+- ``d.key = 'value'`` (aka ``__setattr__``): Maps directly to ``d['key'] =
+  'value'``.
+- ``d.key`` (aka ``__getattr__``): Maps directly to ``d['key']``.
+- ``del d.key`` (aka ``__delattr__``): Maps directly to ``del d['key']``.
+- Collisions between "real" or pre-existing attributes, and
+  attributes-as-dict-keys, always results in the real attribute winning. Thus
+  it isn't possible to use attribute access to access e.g. ``d['get']``.
+
+``Lexicon``
+-----------
+
+Lexicon subclasses from ``AttributeDict`` first, then ``AliasDict``, with the
+end result that attribute access will honor aliases. E.g.:
+
+    d = Lexicon()
+    d.alias('myalias', to='realkey')
+    d.myalias = 'foo'
+    print d.realkey # prints 'foo'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lexicon-1.0.0/dev-requirements.txt 
new/lexicon-2.0.1/dev-requirements.txt
--- old/lexicon-1.0.0/dev-requirements.txt      2016-02-17 23:05:59.000000000 
+0100
+++ new/lexicon-2.0.1/dev-requirements.txt      2021-09-04 00:16:37.000000000 
+0200
@@ -1,12 +1,23 @@
 # Development requirements. There are no runtime/install requirements.
 
-# Python 3 compat spec
-spec>=0.10.0
-# Ourselves
--e .
+# Docs (changelog)
+releases==1.6.3
+Sphinx>=1.4,<1.7
+# Testing
+pytest-relaxed==1.1.5
+# Coverage
+pytest-cov==2.11.1
+codecov==2.1.11
 # Invoke, invocations for release shenanigans etc
--e git+https://github.com/pyinvoke/invoke#egg=invoke
--e git+https://github.com/pyinvoke/invocations#egg=invocations
+invoke==1.6.0
+invocations==2.2.0
 # Release deps
-wheel==0.24
-twine==1.5
+wheel==0.36.2
+twine==1.15.0
+readme_renderer==29.0
+# Formatting
+black==19.10b0
+# Linting
+flake8==3.6.0
+# Ourselves
+-e .
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lexicon-1.0.0/docs/changelog.rst 
new/lexicon-2.0.1/docs/changelog.rst
--- old/lexicon-1.0.0/docs/changelog.rst        1970-01-01 01:00:00.000000000 
+0100
+++ new/lexicon-2.0.1/docs/changelog.rst        2021-09-10 20:30:36.000000000 
+0200
@@ -0,0 +1,24 @@
+=========
+Changelog
+=========
+
+- :release:`2.0.1 <2021-09-10>`
+- :support:`- backported` Fix up some project metadata.
+- :release:`2.0.0 <2021-09-10>`
+- :support:`-` Dropped support for Python <3.6
+- :support:`-` Added a ``_version`` submodule and imported its
+  dunder-attributes into the top level module
+- :support:`-` Migrated CI to CircleCI (from Travis)
+- :support:`-` Migrated tests to pytest(-relaxed)
+- :support:`-` Moved changelog to stub Sphinx project for Releases plugin
+- :support:`-` Changed README to ReStructured Text (from Markdown)
+- :release:`1.0.0 <2016-02-17>`
+- :feature:`9` Updated ``AttributeDict`` (and by extension, ``Lexicon``) so it
+  displays keys in the output of ``dir()`` (since they're also attributes).
+  Thanks to Hugo Oliveira for the initial patch.
+- :support:`-` Bumped to 1.0 because clearly this software is pretty stable!
+- :release:`0.2.0 <2013-03-07>`
+- :bug:`8 major` Python 3 fixes, thanks to Donald Stufft.
+- :release:`0.1.2 <2012-07-10>`
+- :feature:`- backported` Added ``AliasDict.aliases_of(realkey)`` for reverse
+  lookup of what, if any, aliases a given real key has.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lexicon-1.0.0/docs/conf.py 
new/lexicon-2.0.1/docs/conf.py
--- old/lexicon-1.0.0/docs/conf.py      1970-01-01 01:00:00.000000000 +0100
+++ new/lexicon-2.0.1/docs/conf.py      2021-07-09 23:11:54.000000000 +0200
@@ -0,0 +1,35 @@
+from datetime import datetime
+from os import getcwd
+from os.path import abspath, join
+import sys
+
+
+# Core settings
+extensions = [
+    "releases",
+    "sphinx.ext.autodoc",
+]
+templates_path = ["_templates"]
+source_suffix = ".rst"
+master_doc = "index"
+exclude_patterns = ["_build"]
+default_role = "obj"
+
+project = u"Lexicon"
+year = datetime.now().year
+copyright = u"%d Jeff Forcier" % year
+
+# Ensure project directory is on PYTHONPATH for version, autodoc access
+sys.path.insert(0, abspath(join(getcwd(), "..")))
+
+# Enforce use of Alabaster (even on RTD) and configure it
+html_theme = "alabaster"
+html_theme_options = {
+    "description": "Pythonic attribute/alias/etc dict subclasses",
+    "github_user": "bitprophet",
+    "github_repo": "lexicon",
+}
+html_sidebars = {"**": ["about.html", "navigation.html", "searchbox.html"]}
+
+# Other extension configs
+releases_github_path = "bitprophet/lexicon"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lexicon-1.0.0/docs/index.rst 
new/lexicon-2.0.1/docs/index.rst
--- old/lexicon-1.0.0/docs/index.rst    1970-01-01 01:00:00.000000000 +0100
+++ new/lexicon-2.0.1/docs/index.rst    2021-07-08 23:48:18.000000000 +0200
@@ -0,0 +1,11 @@
+=======
+Lexicon
+=======
+
+This is just a dummy Sphinx project for hosting the changelog. Please see the
+top level ``README.rst`` for all non-changelog documentation.
+
+.. toctree::
+    :maxdepth: 1
+
+    changelog
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lexicon-1.0.0/lexicon/__init__.py 
new/lexicon-2.0.1/lexicon/__init__.py
--- old/lexicon-1.0.0/lexicon/__init__.py       2016-02-17 22:27:14.000000000 
+0100
+++ new/lexicon-2.0.1/lexicon/__init__.py       2021-07-09 00:11:33.000000000 
+0200
@@ -1,3 +1,4 @@
+from ._version import __version_info__, __version__  # noqa
 from .attribute_dict import AttributeDict
 from .alias_dict import AliasDict
 
@@ -12,12 +13,12 @@
         # NOTE: could tickle AttributeDict.__init__ instead, in case it ever
         # grows one.
         dict.__init__(self, *args, **kwargs)
-        dict.__setattr__(self, 'aliases', {})
+        dict.__setattr__(self, "aliases", {})
 
     def __getattr__(self, key):
         # Intercept deepcopy/etc driven access to self.aliases when not
         # actually set. (Only a problem for us, due to abovementioned combo of
         # Alias and Attribute Dicts, so not solvable in a parent alone.)
-        if key == 'aliases' and key not in self.__dict__:
+        if key == "aliases" and key not in self.__dict__:
             self.__dict__[key] = {}
         return super(Lexicon, self).__getattr__(key)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lexicon-1.0.0/lexicon/_version.py 
new/lexicon-2.0.1/lexicon/_version.py
--- old/lexicon-1.0.0/lexicon/_version.py       1970-01-01 01:00:00.000000000 
+0100
+++ new/lexicon-2.0.1/lexicon/_version.py       2021-09-10 20:30:41.000000000 
+0200
@@ -0,0 +1,2 @@
+__version_info__ = (2, 0, 1)
+__version__ = ".".join(map(str, __version_info__))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lexicon-1.0.0/lexicon/alias_dict.py 
new/lexicon-2.0.1/lexicon/alias_dict.py
--- old/lexicon-1.0.0/lexicon/alias_dict.py     2014-05-02 00:57:22.000000000 
+0200
+++ new/lexicon-2.0.1/lexicon/alias_dict.py     2021-07-08 23:39:27.000000000 
+0200
@@ -1,11 +1,3 @@
-# Normal import
-try:
-    import six
-# Horrible, awful hack to work when vendorized
-except ImportError:
-    from .. import six
-
-
 class AliasDict(dict):
     def __init__(self, *args, **kwargs):
         super(AliasDict, self).__init__(*args, **kwargs)
@@ -37,19 +29,17 @@
             names.append(key)
         # 'key' is now a realkey, whose aliases are all keys whose value is
         # itself. Filter out the original name given.
-        names.extend([
-            k for k,v
-            in six.iteritems(self.aliases)
-            if v == key and k != name
-        ])
+        names.extend(
+            [k for k, v in self.aliases.items() if v == key and k != name]
+        )
         return names
 
     def _handle(self, key, value, single, multi, unaliased):
         # Attribute existence test required to not blow up when deepcopy'd
-        if key in getattr(self, 'aliases', {}):
+        if key in getattr(self, "aliases", {}):
             target = self.aliases[key]
             # Single-string targets
-            if isinstance(target, six.string_types):
+            if isinstance(target, str):
                 return single(self, target, value)
             # Multi-string targets
             else:
@@ -61,26 +51,31 @@
         else:
             return unaliased(self, key, value)
 
-    def _single(self, target):
-        return isinstance(target, six.string_types)
-
     def __setitem__(self, key, value):
-        def single(d, target, value): d[target] = value
-        def unaliased(d, key, value): super(AliasDict, d).__setitem__(key, 
value)
+        def single(d, target, value):
+            d[target] = value
+
+        def unaliased(d, key, value):
+            super(AliasDict, d).__setitem__(key, value)
+
         return self._handle(key, value, single, None, unaliased)
 
     def __getitem__(self, key):
-        def single(d, target, value): return d[target]
-        def unaliased(d, key, value): return super(AliasDict, 
d).__getitem__(key)
+        def single(d, target, value):
+            return d[target]
+
+        def unaliased(d, key, value):
+            return super(AliasDict, d).__getitem__(key)
 
         def multi(d, target, value):
-            msg = "Multi-target aliases have no well-defined value and can't 
be read."
+            msg = "Multi-target aliases have no well-defined value and can't 
be read."  # noqa
             raise ValueError(msg)
 
         return self._handle(key, None, single, multi, unaliased)
 
     def __contains__(self, key):
-        def single(d, target, value): return target in d
+        def single(d, target, value):
+            return target in d
 
         def multi(d, target, value):
             return all(subkey in self for subkey in self.aliases[key])
@@ -91,7 +86,8 @@
         return self._handle(key, None, single, multi, unaliased)
 
     def __delitem__(self, key):
-        def single(d, target, value): del d[target]
+        def single(d, target, value):
+            del d[target]
 
         def unaliased(d, key, value):
             return super(AliasDict, d).__delitem__(key)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lexicon-1.0.0/lexicon.egg-info/PKG-INFO 
new/lexicon-2.0.1/lexicon.egg-info/PKG-INFO
--- old/lexicon-1.0.0/lexicon.egg-info/PKG-INFO 2016-02-17 23:10:28.000000000 
+0100
+++ new/lexicon-2.0.1/lexicon.egg-info/PKG-INFO 2021-09-10 20:38:28.000000000 
+0200
@@ -1,96 +1,127 @@
-Metadata-Version: 1.1
+Metadata-Version: 2.1
 Name: lexicon
-Version: 1.0.0
+Version: 2.0.1
 Summary: Powerful dict subclass(es) with aliasing & attribute access
-Home-page: https://github.com/bitprophet/lexicon
+Home-page: https://github.com/bitprophet/lexicon#what
 Author: Jeff Forcier
 Author-email: j...@bitprophet.org
 License: BSD
-Description: ## WHAT
-        
-        Lexicon is a simple Python 2.6+ and 3.3+ compatible collection of 
`dict`
-        subclasses providing extra power:
-        
-        * `AliasDict`, a dictionary supporting both simple and complex key 
aliasing:
-            * Alias a single key to another key, so that e.g. `mydict['bar']` 
points to
-            `mydict['foo']`, for both reads and writes.
-            * Alias a single key to a list of other keys, for writing only, 
e.g. with
-            `active_groups = AliasDict({'ops': True, 'biz': True, 'dev': True,
-            'product': True})` one can make an alias `'tech'` mapping to 
`('ops',
-            'dev')` and then e.g. `active_groups['tech'] = False`.
-            * Aliasing is recursive: an alias pointing to another alias will 
behave as
-            if it points to the other alias' target.
-        * `AttributeDict`, supporting attribute read & write access, e.g.
-          `mydict = AttributeDict({'foo': 'bar'})` exhibits `mydict.foo` and
-          `mydict.foo = 'new value'`.
-        * `Lexicon`, a subclass of both of the above which exhibits both sets 
of
+Project-URL: Source, https://github.com/bitprophet/lexicon
+Project-URL: Changelog, 
https://github.com/bitprophet/lexicon/blob/main/docs/changelog.rst
+Project-URL: CI, https://app.circleci.com/pipelines/github/bitprophet/lexicon
+Description: |version| |python| |license| |ci| |coverage|
+        
+        .. |version| image:: https://img.shields.io/pypi/v/lexicon
+            :target: https://pypi.org/project/lexicon/
+            :alt: PyPI - Package Version
+        .. |python| image:: https://img.shields.io/pypi/pyversions/lexicon
+            :target: https://pypi.org/project/lexicon/
+            :alt: PyPI - Python Version
+        .. |license| image:: https://img.shields.io/pypi/l/lexicon
+            :target: https://github.com/bitprophet/lexicon/blob/main/LICENSE
+            :alt: PyPI - License
+        .. |ci| image:: 
https://img.shields.io/circleci/build/github/bitprophet/lexicon/main
+            :target: 
https://app.circleci.com/pipelines/github/bitprophet/lexicon
+            :alt: CircleCI
+        .. |coverage| image:: 
https://img.shields.io/codecov/c/gh/bitprophet/lexicon
+            :target: https://app.codecov.io/gh/bitprophet/lexicon
+            :alt: Codecov
+        
+        WHAT
+        ====
+        
+        Lexicon is a simple collection of Python ``dict`` subclasses providing 
extra
+        power:
+        
+        - ``AliasDict``, a dictionary supporting both simple and complex key 
aliasing:
+        
+            - Alias a single key to another key, so that e.g. 
``mydict['bar']`` points
+              to ``mydict['foo']``, for both reads and writes.
+            - Alias a single key to a list of other keys, for writing only, 
e.g. with
+              ``active_groups = AliasDict({'ops': True, 'biz': True, 'dev': 
True,
+              'product': True})`` one can make an alias ``'tech'`` mapping to 
``('ops',
+              'dev')`` and then e.g. ``active_groups['tech'] = False``.
+            - Aliasing is recursive: an alias pointing to another alias will 
behave as
+              if it points to the other alias' target.
+        
+        - ``AttributeDict``, supporting attribute read & write access, e.g. 
``mydict =
+          AttributeDict({'foo': 'bar'})`` exhibits ``mydict.foo`` and 
``mydict.foo =
+          'new value'``.
+        - ``Lexicon``, a subclass of both of the above which exhibits both 
sets of
           behavior.
         
-        ## HOW
+        HOW
+        ===
         
-        * `pip install lexicon`
-        * `from lexicon import Lexicon` (or one of the superclasses)
-        * Use as needed.
-        
-        You can install the [development
-        
version](https://github.com/bitprophet/lexicon/tarball/master#egg=lexicon-dev)
-        via `pip install lexicon==dev`.
+        - ``pip install lexicon``
+        - ``from lexicon import Lexicon`` (or one of the superclasses)
+        - Use as needed.
         
         If you have a clone of the source repository, you can run the tests 
like so:
         
-        * `pip install -r dev-requirements.txt`
-        * `spec`
+        - ``pip install -r dev-requirements.txt``
+        - ``inv test``
         
-        ## API
+        API
+        ===
         
-        ### `AliasDict`
+        ``AliasDict``
+        -------------
         
-        In all examples, `'myalias'` is the alias and `'realkey'` is the 
"real",
+        In all examples, ``'myalias'`` is the alias and ``'realkey'`` is the 
"real",
         unaliased key.
         
-        * `alias(from_'myalias', to='realkey')`: Alias `myalias` to `realkey` 
so
-          `d['myalias']` behaves exactly like `d['realkey']` for both reads 
and writes.
-            * `from_` is the first keyword argument, but typically it can be 
omitted
-            and still reads fine. See below examples for this usage.
-          See below for details on how an alias affects other dict operations.
-        * `alias('myalias', to=('realkey', 'otherrealkey'))`: Alias `myalias` 
to
-          both `realkey` and `otherrealkey`. As you might expect, this only 
works well
-          for writes, as there is never any guarantee that all targets of the 
alias
-          will contain the same value.
-        * `unalias('myalias')`: Removes the `myalias` alias; any subsequent
-          reads/writes to `myalias` will behave as normal for a regular `dict`.
-        * `'myalias' in d` (aka `__contains__`): Returns True when given an 
alias, so
-          if `myalias` is an alias to some other key, dictionary membership 
tests will
-          behave as if `myalias` is set.
-        * `del d['myalias']` (aka `__delitem__`): This effectively becomes `del
-          d['realkey']` -- to remove the alias itself, use `unalias()`.
-        * `del d['realkey']`: Deletes the real key/value pair (i.e. it calls
-          `dict.__del__`) but doesn't touch any aliases pointing to `realkey`.
-            * As a result, "dangling" aliases pointing to nonexistent keys 
will raise
-            `KeyError` on access, but will continue working if the target key 
is
-            repopulated later.
+        - ``alias(from_'myalias', to='realkey')``: Alias ``myalias`` to 
``realkey`` so
+          ``d['myalias']`` behaves exactly like ``d['realkey']`` for both 
reads and
+          writes.
+          
+            - ``from_`` is the first keyword argument, but typically it can be 
omitted
+              and still reads fine. See below examples for this usage. See 
below for
+              details on how an alias affects other dict operations.
+        
+        - ``alias('myalias', to=('realkey', 'otherrealkey'))``: Alias 
``myalias`` to
+          both ``realkey`` and ``otherrealkey``. As you might expect, this 
only works
+          well for writes, as there is never any guarantee that all targets of 
the
+          alias will contain the same value.
+        - ``unalias('myalias')``: Removes the ``myalias`` alias; any subsequent
+          reads/writes to ``myalias`` will behave as normal for a regular 
``dict``.
+        - ``'myalias' in d`` (aka ``__contains__``): Returns True when given 
an alias,
+          so if ``myalias`` is an alias to some other key, dictionary 
membership tests
+          will behave as if ``myalias`` is set.
+        - ``del d['myalias']`` (aka ``__delitem__``): This effectively becomes 
``del
+          d['realkey']`` -- to remove the alias itself, use ``unalias()``.
+        - ``del d['realkey']``: Deletes the real key/value pair (i.e. it calls
+          ``dict.__del__``) but doesn't touch any aliases pointing to 
``realkey``.
+        
+            - As a result, "dangling" aliases pointing to nonexistent keys 
will raise
+              ``KeyError`` on access, but will continue working if the target 
key is
+              repopulated later.
         
         Caveats:
         
-        * Because of the single-key/multi-key duality, `AliasDict` is 
incapable of
-          honoring non-string-type keys when aliasing (it must test 
`isinstance(key,
-          basestring)` to tell strings apart from non-string iterables).
-            * `AliasDict` instances may still *use* non-string keys, of course 
-- it
-            just can't use them as alias targets.
-        
-        ### `AttributeDict`
-        
-        * `d.key = 'value'` (aka `__setattr__`): Maps directly to `d['key'] = 
'value'`.
-        * `d.key` (aka `__getattr__`): Maps directly to `d['key']`.
-        * `del d.key` (aka `__delattr__`): Maps directly to `del d['key']`.
-        * Collisions between "real" or pre-existing attributes, and
+        - Because of the single-key/multi-key duality, ``AliasDict`` is 
incapable of
+          honoring non-string-type keys when aliasing (it must test 
``isinstance(key,
+          basestring)`` to tell strings apart from non-string iterables).
+        
+            - ``AliasDict`` instances may still *use* non-string keys, of 
course -- it
+              just can't use them as alias targets.
+        
+        ``AttributeDict``
+        -----------------
+        
+        - ``d.key = 'value'`` (aka ``__setattr__``): Maps directly to 
``d['key'] =
+          'value'``.
+        - ``d.key`` (aka ``__getattr__``): Maps directly to ``d['key']``.
+        - ``del d.key`` (aka ``__delattr__``): Maps directly to ``del 
d['key']``.
+        - Collisions between "real" or pre-existing attributes, and
           attributes-as-dict-keys, always results in the real attribute 
winning. Thus
-          it isn't possible to use attribute access to access e.g. `d['get']`.
+          it isn't possible to use attribute access to access e.g. 
``d['get']``.
         
-        ### `Lexicon`
+        ``Lexicon``
+        -----------
         
-        Lexicon subclasses from `AttributeDict` first, then `AliasDict`, with 
the end
-        result that attribute access will honor aliases. E.g.:
+        Lexicon subclasses from ``AttributeDict`` first, then ``AliasDict``, 
with the
+        end result that attribute access will honor aliases. E.g.:
         
             d = Lexicon()
             d.alias('myalias', to='realkey')
@@ -106,13 +137,12 @@
 Classifier: Operating System :: Unix
 Classifier: Operating System :: POSIX
 Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.6
-Classifier: Programming Language :: Python :: 2.7
 Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.3
-Classifier: Programming Language :: Python :: 3.4
-Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
 Classifier: Topic :: Software Development
 Classifier: Topic :: Software Development :: Libraries
 Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Description-Content-Type: text/x-rst
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lexicon-1.0.0/lexicon.egg-info/SOURCES.txt 
new/lexicon-2.0.1/lexicon.egg-info/SOURCES.txt
--- old/lexicon-1.0.0/lexicon.egg-info/SOURCES.txt      2016-02-17 
23:10:28.000000000 +0100
+++ new/lexicon-2.0.1/lexicon.egg-info/SOURCES.txt      2021-09-10 
20:38:28.000000000 +0200
@@ -1,16 +1,19 @@
 LICENSE
 MANIFEST.in
-README.md
+README.rst
 dev-requirements.txt
 setup.cfg
 setup.py
+docs/changelog.rst
+docs/conf.py
+docs/index.rst
 lexicon/__init__.py
+lexicon/_version.py
 lexicon/alias_dict.py
 lexicon/attribute_dict.py
 lexicon.egg-info/PKG-INFO
 lexicon.egg-info/SOURCES.txt
 lexicon.egg-info/dependency_links.txt
-lexicon.egg-info/requires.txt
 lexicon.egg-info/top_level.txt
 tests/alias_dict.py
 tests/attribute_dict.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lexicon-1.0.0/lexicon.egg-info/requires.txt 
new/lexicon-2.0.1/lexicon.egg-info/requires.txt
--- old/lexicon-1.0.0/lexicon.egg-info/requires.txt     2016-02-17 
23:10:28.000000000 +0100
+++ new/lexicon-2.0.1/lexicon.egg-info/requires.txt     1970-01-01 
01:00:00.000000000 +0100
@@ -1 +0,0 @@
-six
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lexicon-1.0.0/setup.cfg new/lexicon-2.0.1/setup.cfg
--- old/lexicon-1.0.0/setup.cfg 2016-02-17 23:10:28.000000000 +0100
+++ new/lexicon-2.0.1/setup.cfg 2021-09-10 20:38:28.000000000 +0200
@@ -1,8 +1,10 @@
 [bdist_wheel]
 universal = 1
 
+[metadata]
+license_file = LICENSE
+
 [egg_info]
 tag_build = 
 tag_date = 0
-tag_svn_revision = 0
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lexicon-1.0.0/setup.py new/lexicon-2.0.1/setup.py
--- old/lexicon-1.0.0/setup.py  2016-02-17 22:39:42.000000000 +0100
+++ new/lexicon-2.0.1/setup.py  2021-09-10 20:31:50.000000000 +0200
@@ -1,48 +1,51 @@
 #!/usr/bin/env python
 
 import os
-import sys
 
-# Support setuptools or distutils
-try:
-    from setuptools import setup
-except ImportError:
-    from distutils.core import setup
+from setuptools import setup
 
-long_description = open(os.path.join(os.path.dirname(__file__), 
'README.md')).read()
+# Version info -- read without importing
+_locals = {}
+with open("lexicon/_version.py") as fp:
+    exec(fp.read(), None, _locals)
+version = _locals["__version__"]
+
+# Readme
+with open(os.path.join(os.path.dirname(__file__), "README.rst")) as fd:
+    long_description = fd.read()
 
 setup(
-    name='lexicon',
-    version="1.0.0",
-    description='Powerful dict subclass(es) with aliasing & attribute access',
-    license='BSD',
-
+    name="lexicon",
+    version=version,
+    description="Powerful dict subclass(es) with aliasing & attribute access",
+    license="BSD",
     long_description=long_description,
-    author='Jeff Forcier',
-    author_email='j...@bitprophet.org',
-    url='https://github.com/bitprophet/lexicon',
-
+    long_description_content_type="text/x-rst",
+    author="Jeff Forcier",
+    author_email="j...@bitprophet.org",
+    url="https://github.com/bitprophet/lexicon#what";,
+    project_urls={
+        "Source": "https://github.com/bitprophet/lexicon";,
+        "Changelog": 
"https://github.com/bitprophet/lexicon/blob/main/docs/changelog.rst";,
+        "CI": "https://app.circleci.com/pipelines/github/bitprophet/lexicon";,
+    },
     packages=["lexicon"],
-    install_requires=["six"],
-
     classifiers=[
-          'Development Status :: 5 - Production/Stable',
-          'Environment :: Console',
-          'Intended Audience :: Developers',
-          'License :: OSI Approved :: BSD License',
-          'Operating System :: MacOS :: MacOS X',
-          'Operating System :: Unix',
-          'Operating System :: POSIX',
-          'Programming Language :: Python',
-          'Programming Language :: Python :: 2',
-          'Programming Language :: Python :: 2.6',
-          'Programming Language :: Python :: 2.7',
-          'Programming Language :: Python :: 3',
-          'Programming Language :: Python :: 3.3',
-          'Programming Language :: Python :: 3.4',
-          'Programming Language :: Python :: 3.5',
-          'Topic :: Software Development',
-          'Topic :: Software Development :: Libraries',
-          'Topic :: Software Development :: Libraries :: Python Modules',
+        "Development Status :: 5 - Production/Stable",
+        "Environment :: Console",
+        "Intended Audience :: Developers",
+        "License :: OSI Approved :: BSD License",
+        "Operating System :: MacOS :: MacOS X",
+        "Operating System :: Unix",
+        "Operating System :: POSIX",
+        "Programming Language :: Python",
+        "Programming Language :: Python :: 3",
+        "Programming Language :: Python :: 3.6",
+        "Programming Language :: Python :: 3.7",
+        "Programming Language :: Python :: 3.8",
+        "Programming Language :: Python :: 3.9",
+        "Topic :: Software Development",
+        "Topic :: Software Development :: Libraries",
+        "Topic :: Software Development :: Libraries :: Python Modules",
     ],
 )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lexicon-1.0.0/tests/alias_dict.py 
new/lexicon-2.0.1/tests/alias_dict.py
--- old/lexicon-1.0.0/tests/alias_dict.py       2016-02-17 22:26:09.000000000 
+0100
+++ new/lexicon-2.0.1/tests/alias_dict.py       2021-07-08 23:39:27.000000000 
+0200
@@ -1,118 +1,133 @@
 import copy
 
-import six
-from spec import Spec, eq_, ok_, raises, skip
+from pytest import raises
 
 from lexicon import AliasDict
 
 
-class AliasDict_(Spec):
+class AliasDict_:
     class alias:
         def allows_aliasing_of_single_target_key(self):
             ad = AliasDict()
-            ad.alias(from_='myalias', to='realkey')
-            ad['realkey'] = 'value'
-            eq_(ad['myalias'], 'value')
+            ad.alias(from_="myalias", to="realkey")
+            ad["realkey"] = "value"
+            assert ad["myalias"] == "value"
 
         def allows_aliasing_of_multiple_target_keys(self):
             ad = AliasDict()
-            ad.alias(from_='myalias', to=('key1', 'key2'))
-            ad['key1'] = ad['key2'] = False
-            assert not ad['key1']
-            ad['myalias'] = True
-            assert ad['key1'] and ad['key2']
+            ad.alias(from_="myalias", to=("key1", "key2"))
+            ad["key1"] = ad["key2"] = False
+            assert not ad["key1"]
+            ad["myalias"] = True
+            assert ad["key1"] and ad["key2"]
 
     class unalias:
         def unsets_aliases(self):
             ad = AliasDict()
-            ad['realkey'] = 'value'
-            ad.alias('myalias', to='realkey')
-            eq_(ad['myalias'], 'value')
-            ad.unalias('myalias')
-            assert 'myalias' not in ad
+            ad["realkey"] = "value"
+            ad.alias("myalias", to="realkey")
+            assert ad["myalias"] == "value"
+            ad.unalias("myalias")
+            assert "myalias" not in ad
 
-        @raises(KeyError)
         def raises_KeyError_on_nonexistent_alias(self):
             ad = AliasDict()
-            ad.unalias('lol no')
+            with raises(KeyError):
+                ad.unalias("lol no")
 
     class aliases_of:
         def setup(self):
             self.ad = AliasDict()
 
         def returns_list_of_aliases_for_given_real_key(self):
-            eq_(self.ad.aliases_of('realkey'), [])
-            self.ad.alias('myalias', to='realkey')
-            eq_(self.ad.aliases_of('realkey'), ['myalias'])
-            self.ad.unalias('myalias')
-            eq_(self.ad.aliases_of('realkey'), [])
+            assert self.ad.aliases_of("realkey") == []
+            self.ad.alias("myalias", to="realkey")
+            assert self.ad.aliases_of("realkey") == ["myalias"]
+            self.ad.unalias("myalias")
+            assert self.ad.aliases_of("realkey") == []
 
         def returns_empty_list_for_unaliased_keys(self):
-            self.ad['realkey'] = 5
-            eq_(self.ad.aliases_of('realkey'), [])
+            self.ad["realkey"] = 5
+            assert self.ad.aliases_of("realkey") == []
 
         def returns_multi_item_list_for_multiple_aliases(self):
-            self.ad.alias('alias1', to='realkey')
-            self.ad.alias('alias2', to='realkey')
-            eq_(set(self.ad.aliases_of('realkey')), set(['alias1', 'alias2']))
+            self.ad.alias("alias1", to="realkey")
+            self.ad.alias("alias2", to="realkey")
+            assert set(self.ad.aliases_of("realkey")), set(
+                ["alias1" == "alias2"]
+            )
 
         def returns_list_of_aliases_for_alias(self):
-            self.ad.alias('myalias', to='realkey')
-            result = set(self.ad.aliases_of('myalias'))
-            expected = set(['realkey'])
-            eq_(result, expected)
+            self.ad.alias("myalias", to="realkey")
+            result = set(self.ad.aliases_of("myalias"))
+            expected = set(["realkey"])
+            assert result == expected
 
-    def membership_tests(self):
+    def single_membership_tests(self):
         ad = AliasDict()
-        ad.alias('myalias', to='realkey')
-        ad['realkey'] = 'value'
-        assert 'myalias' in ad
+        ad.alias("myalias", to="realkey")
+        ad["realkey"] = "value"
+        assert "myalias" in ad
+
+    def multi_membership_test(self):
+        ad = AliasDict()
+        ad.alias("multi-alias", to=("key1", "key2"))
+        # No targets actually exist: false
+        assert "multi-alias" not in ad
+        # One target exists: still false
+        ad["key1"] = 5
+        assert "multi-alias" not in ad
+        # All exist: true
+        ad["key2"] = 5
+        assert "multi-alias" in ad
 
     def key_deletion(self):
         ad = AliasDict()
-        ad.alias('myalias', to='realkey')
-        ad['realkey'] = 'value'
-        assert 'realkey' in ad
-        del ad['myalias']
-        assert 'realkey' not in ad
-        assert 'myalias' not in ad
+        ad.alias("myalias", to="realkey")
+        ad["realkey"] = "value"
+        assert "realkey" in ad
+        del ad["myalias"]
+        assert "realkey" not in ad
+        assert "myalias" not in ad
 
-    @raises(ValueError)
     def access_to_multi_target_aliases_is_undefined(self):
         ad = AliasDict()
-        ad.alias('myalias', to=('key1', 'key2'))
-        ad['key1'] = ad['key2'] = 5
-        ad['myalias']
+        ad.alias("myalias", to=("key1", "key2"))
+        ad["key1"] = ad["key2"] = 5
+        with raises(ValueError):
+            ad["myalias"]
 
     class dangling_aliases:
         "dangling aliases"
-        @raises(KeyError)
+
         def raise_KeyError_on_access(self):
             ad = AliasDict()
-            ad.alias('myalias', to='realkey')
-            assert 'realkey' not in ad
-            ad['myalias']
+            ad.alias("myalias", to="realkey")
+            assert "realkey" not in ad
+            with raises(KeyError):
+                ad["myalias"]
 
-        @raises(KeyError)
         def caused_by_removal_of_target_key(self):
-            # TODO: this test probably false-passes if any line but the last 
raises
-            # KeyError by accident...
+            # TODO: this test probably false-passes if any line but the last
+            # raises KeyError by accident...
             ad = AliasDict()
-            ad.alias('myalias', to='realkey')
-            ad['realkey'] = 'value'
-            assert 'realkey' in ad
-            eq_(ad['myalias'], 'value')
-            del ad['realkey']
-            ad['myalias']
+            ad.alias("myalias", to="realkey")
+            ad["realkey"] = "value"
+            assert "realkey" in ad
+            assert ad["myalias"] == "value"
+            del ad["realkey"]
+            with raises(KeyError):
+                ad["myalias"]
 
     class recursive_aliasing:
         "recursive aliasing"
+
         def _recursive_aliases(self):
             ad = AliasDict()
-            ad.alias('alias1', to='realkey')
-            ad.alias('alias2', to='alias1')
-            ad['alias2'] = 'value'
-            assert ad['alias1'] == ad['alias2'] == ad['realkey'] == 'value'
+            ad.alias("alias1", to="realkey")
+            ad.alias("alias2", to="alias1")
+            ad["alias2"] = "value"
+            assert ad["alias1"] == ad["alias2"] == ad["realkey"] == "value"
             return ad
 
         def works_in_base_case(self):
@@ -120,46 +135,44 @@
 
         def unalias_is_not_recursive(self):
             ad = self._recursive_aliases()
-            ad.unalias('alias2')
-            assert 'alias1' in ad
-            eq_(ad['alias1'], 'value')
+            ad.unalias("alias2")
+            assert "alias1" in ad
+            assert ad["alias1"] == "value"
 
         def deletion_is_recursive(self):
             ad = self._recursive_aliases()
-            del ad['alias2']
-            assert 'realkey' not in ad
-            ad['realkey'] = 'newvalue'
-            assert 'alias1' in ad
-            eq_(ad['alias1'], 'newvalue')
+            del ad["alias2"]
+            assert "realkey" not in ad
+            ad["realkey"] = "newvalue"
+            assert "alias1" in ad
+            assert ad["alias1"] == "newvalue"
 
     def deepcopy_copies_aliases_correctly(self):
-        a1 = AliasDict({'key1': 'val1', 'key2': 'val2'})
-        a1.alias('myalias', to='key1')
-        eq_(a1['myalias'], 'val1')
+        a1 = AliasDict({"key1": "val1", "key2": "val2"})
+        a1.alias("myalias", to="key1")
+        assert a1["myalias"] == "val1"
         a2 = copy.deepcopy(a1)
-        assert 'key1' in a2
-        eq_(a2['key2'], 'val2')
-        a1.unalias('myalias')
-        assert 'myalias' not in a1
-        assert 'myalias' in a2
-        eq_(a2['myalias'], 'val1')
+        assert "key1" in a2
+        assert a2["key2"] == "val2"
+        a1.unalias("myalias")
+        assert "myalias" not in a1
+        assert "myalias" in a2
+        assert a2["myalias"] == "val1"
 
     class aliases_are_not_real_keys:
         "aliases are not real keys"
 
         def setup(self):
-            self.a = AliasDict({'key1': 'val1', 'key2': 'val2'})
-            self.a.alias('myalias', 'key1')
+            self.a = AliasDict({"key1": "val1", "key2": "val2"})
+            self.a.alias("myalias", "key1")
 
         def keys_returns_only_real_keys(self):
             "keys() only returns real keys, not aliases"
-            assert 'myalias' not in self.a.keys()
-            assert 'key1' in self.a.keys()
-            assert 'key2' in self.a.keys()
+            assert "myalias" not in self.a.keys()
+            assert "key1" in self.a.keys()
+            assert "key2" in self.a.keys()
 
         def items_returns_only_real_keys(self):
             "items() and iteritems() only return real keys, not aliases"
-            assert ('key1', 'val1') in self.a.items()
-            assert ('key2', 'val2') in six.iteritems(self.a)
-            assert ('myalias', 'val1') not in self.a.items()
-            assert ('myalias', 'val1') not in six.iteritems(self.a)
+            assert ("key1", "val1") in self.a.items()
+            assert ("myalias", "val1") not in self.a.items()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lexicon-1.0.0/tests/attribute_dict.py 
new/lexicon-2.0.1/tests/attribute_dict.py
--- old/lexicon-1.0.0/tests/attribute_dict.py   2016-02-17 22:27:54.000000000 
+0100
+++ new/lexicon-2.0.1/tests/attribute_dict.py   2021-07-08 23:39:27.000000000 
+0200
@@ -1,46 +1,43 @@
 import copy
 
-import six
-from spec import Spec, eq_, ok_
-
 from lexicon import AttributeDict
 
 
-class AttributeDict_(Spec):
+class AttributeDict_:
     def allows_attribute_reads(self):
         ad = AttributeDict()
-        ad['foo'] = 'bar'
-        eq_(ad['foo'], ad.foo)
+        ad["foo"] = "bar"
+        assert ad["foo"] == ad.foo
 
     def allows_attribute_writes(self):
         ad = AttributeDict()
-        ad['foo'] = 'bar'
-        eq_(ad['foo'], 'bar')
-        ad.foo = 'notbar'
-        eq_(ad['foo'], 'notbar')
+        ad["foo"] = "bar"
+        assert ad["foo"] == "bar"
+        ad.foo = "notbar"
+        assert ad["foo"] == "notbar"
 
     def honors_attribute_deletion(self):
         ad = AttributeDict()
-        ad['foo'] = 'bar'
-        eq_(ad.foo, 'bar')
+        ad["foo"] = "bar"
+        assert ad.foo == "bar"
         del ad.foo
-        assert 'foo' not in ad
+        assert "foo" not in ad
 
     def ensures_real_attributes_win(self):
         ad = AttributeDict()
-        ad['get'] = 'not-a-method'
+        ad["get"] = "not-a-method"
         assert callable(ad.get)
-        assert not isinstance(ad.get, six.string_types)
+        assert not isinstance(ad.get, str)
 
     def ensure_deepcopy_works(self):
         ad = AttributeDict()
-        ad['foo'] = 'bar'
-        eq_(ad.foo, 'bar')
+        ad["foo"] = "bar"
+        assert ad.foo == "bar"
         ad2 = copy.deepcopy(ad)
-        ad2.foo = 'biz'
+        ad2.foo = "biz"
         assert ad2.foo != ad.foo
 
     def dir_shows_keys(self):
         ad = AttributeDict()
-        ad['foo'] = 'bar'
-        assert 'foo' in dir(ad)
+        ad["foo"] = "bar"
+        assert "foo" in dir(ad)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lexicon-1.0.0/tests/lexicon_.py 
new/lexicon-2.0.1/tests/lexicon_.py
--- old/lexicon-1.0.0/tests/lexicon_.py 2016-02-17 22:27:54.000000000 +0100
+++ new/lexicon-2.0.1/tests/lexicon_.py 2021-07-08 23:39:27.000000000 +0200
@@ -1,47 +1,45 @@
 import copy
 
-from spec import Spec, eq_
-
 from lexicon import Lexicon
 
 
-class Lexicon_(Spec):
+class Lexicon_:
     def attributes_work(self):
-        l = Lexicon()
-        l.foo = 'bar'
-        eq_(l['foo'], l.foo)
+        lex = Lexicon()
+        lex.foo = "bar"
+        assert lex["foo"] == lex.foo
 
     def aliases_work(self):
-        l = Lexicon()
-        l.alias('foo', to='bar')
-        l['bar'] = 'value'
-        assert l['foo'] == l['bar'] == 'value'
+        lex = Lexicon()
+        lex.alias("foo", to="bar")
+        lex["bar"] = "value"
+        assert lex["foo"] == lex["bar"] == "value"
 
     def aliases_appear_in_attributes(self):
-        l = Lexicon()
-        l.alias('foo', to='bar')
-        l.foo = 'value'
-        assert l.foo == l.bar == l['foo'] == l['bar'] == 'value'
+        lex = Lexicon()
+        lex.alias("foo", to="bar")
+        lex.foo = "value"
+        assert lex.foo == lex.bar == lex["foo"] == lex["bar"] == "value"
 
     def aliased_real_attributes_do_not_override_real_attributes(self):
-        l = Lexicon()
-        l.alias('get', to='notget')
-        l.notget = 'value'
-        assert callable(l.get)
-        assert l.get != 'value'
+        lex = Lexicon()
+        lex.alias("get", to="notget")
+        lex.notget = "value"
+        assert callable(lex.get)
+        assert lex.get != "value"
 
     def ensure_deepcopy_works(self):
-        l = Lexicon()
-        l['foo'] = 'bar'
-        eq_(l.foo, 'bar')
-        l2 = copy.deepcopy(l)
-        l2.foo = 'biz'
-        assert l2.foo != l.foo
+        lex = Lexicon()
+        lex["foo"] = "bar"
+        assert lex.foo == "bar"
+        lex2 = copy.deepcopy(lex)
+        lex2.foo = "biz"
+        assert lex2.foo != lex.foo
 
     def dir_only_shows_real_keys(self):
         "dir() only shows real keys-as-attrs, not aliases"
-        a = Lexicon({'key1': 'val1', 'key2': 'val2'})
-        a.alias('myalias', 'key1')
-        assert 'key1' in dir(a)
-        assert 'key2' in dir(a)
-        assert 'myalias' not in dir(a)
+        a = Lexicon({"key1": "val1", "key2": "val2"})
+        a.alias("myalias", "key1")
+        assert "key1" in dir(a)
+        assert "key2" in dir(a)
+        assert "myalias" not in dir(a)

Reply via email to