From 612bda9314b032852d433dd7275c0c43cf4db77f Mon Sep 17 00:00:00 2001
From: Christian Hill <xn.hill@gmail.com>
Date: Fri, 29 Jul 2022 19:52:41 +0200
Subject: [PATCH] Rearrange for pypi packaging

---
 gpl.txt => LICENSE                            |  0
 README.md                                     | 27 --------
 README.rst                                    | 68 +++++++++++++++++++
 pyproject.toml                                |  3 +
 setup.cfg                                     |  3 +
 setup.py                                      | 55 ++++++++++++---
 {pyqn => src/pyqn}/__init__.py                |  0
 {pyqn => src/pyqn}/atom_unit.py               |  0
 {pyqn => src/pyqn}/base_unit.py               |  4 +-
 {pyqn => src/pyqn}/dimensions.py              |  0
 {pyqn => src/pyqn}/list_base_units.py         |  0
 {pyqn => src/pyqn}/qn_array.py                |  0
 {pyqn => src/pyqn}/quantity.py                | 34 +++++-----
 {pyqn => src/pyqn}/si.py                      |  0
 {pyqn => src/pyqn}/symbol.py                  |  0
 {pyqn => src/pyqn}/units.py                   |  0
 {pyqn/tests => tests}/test_conversions.py     |  2 +-
 {pyqn/tests => tests}/test_quantity.py        |  8 +--
 {pyqn/tests => tests}/test_units.py           |  4 +-
 .../tests => tests}/test_units_collisions.py  |  6 +-
 20 files changed, 147 insertions(+), 67 deletions(-)
 rename gpl.txt => LICENSE (100%)
 delete mode 100644 README.md
 create mode 100644 README.rst
 create mode 100644 pyproject.toml
 create mode 100644 setup.cfg
 rename {pyqn => src/pyqn}/__init__.py (100%)
 rename {pyqn => src/pyqn}/atom_unit.py (100%)
 rename {pyqn => src/pyqn}/base_unit.py (99%)
 rename {pyqn => src/pyqn}/dimensions.py (100%)
 rename {pyqn => src/pyqn}/list_base_units.py (100%)
 rename {pyqn => src/pyqn}/qn_array.py (100%)
 rename {pyqn => src/pyqn}/quantity.py (93%)
 rename {pyqn => src/pyqn}/si.py (100%)
 rename {pyqn => src/pyqn}/symbol.py (100%)
 rename {pyqn => src/pyqn}/units.py (100%)
 rename {pyqn/tests => tests}/test_conversions.py (97%)
 rename {pyqn/tests => tests}/test_quantity.py (94%)
 rename {pyqn/tests => tests}/test_units.py (97%)
 rename {pyqn/tests => tests}/test_units_collisions.py (90%)

diff --git a/gpl.txt b/LICENSE
similarity index 100%
rename from gpl.txt
rename to LICENSE
diff --git a/README.md b/README.md
deleted file mode 100644
index ce041ff..0000000
--- a/README.md
+++ /dev/null
@@ -1,27 +0,0 @@
-# pyqn
-A Python package for handling physical units and quantities.
-
-A very quick overview:
-
-    In [1]: from pyqn.units import Units
-
-    In [2]: u1 = Units('km')
-
-    In [3]: u2 = Units('hr')
-
-    In [4]: u3 = u1/u2
-
-    In [5]: print(u3)
-    km.hr-1
-
-    In [6]: u4 = Units('m/s')
-
-    In [7]: u3.conversion(u4)        # OK: can convert from km/hr to m/s
-    Out[7]: 0.2777777777777778
-
-    In [8]: u3.conversion(u2)        #  Oops: can't convert from km/hr to m!
-    ...
-    UnitsError: Failure in units conversion: units km.hr-1[L.T-1] and hr[T] have
-    different dimensions
-
-For more information and examples, see http://christianhill.co.uk/projects/pyqn
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..f5c6fe8
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,68 @@
+********************
+Introduction to PyQn
+********************
+
+
+
+PyQn is a Python package for parsing, validating, manipulating and
+transforming physical quantities and their units.
+
+Units are specified as strings using a simple and flexible syntax,
+and may be compared, output in different formats and manipulated using a
+variety of predefined Python methods.
+
+
+
+Installation:
+=============
+
+The PyQn package can be installed either from PyPI_ using pip
+
+.. code-block:: bash
+
+    python3 -m pip install pyqn
+
+or from the source by running (one of the two) from the project source directory.
+
+.. code-block:: bash
+
+    # either
+    python setup.py install
+
+    # or
+    python3 -m pip install .
+
+
+
+Examples:
+=========
+
+Units
+-----
+The units of physical quantities are represented by the ``Units`` class. A
+``Units`` object is instantiated from a valid units string and supports ions,
+isotopologues, as well as a few special species. This object contains
+attributes including the dimensions, HTML and LaTeX representations, and
+methods for conversion to different compatible units.
+
+.. code-block:: pycon
+
+    >>> from pyqn.units import Units
+    >>> u1 = Units('km')
+
+    >>> u2 = Units('hr')
+
+    >>> u3 = u1/u2
+
+    >>> print(u3)
+    km.hr-1
+
+    >>> u4 = Units('m/s')
+
+    >>> u3.conversion(u4)        # OK: can convert from km/hr to m/s
+    Out[7]: 0.2777777777777778
+
+    >>> u3.conversion(u2)        #  Oops: can't convert from km/hr to m!
+    ...
+    UnitsError: Failure in units conversion: units km.hr-1[L.T-1] and hr[T] have
+    different dimensions
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..9f92222
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,3 @@
+[build-system]
+requires = ["setuptools>=43", "wheel"]
+build-backend = "setuptools.build_meta"
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..41cf804
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,3 @@
+[metadata]
+# This includes the license file(s) in the wheel (requires setuptools>=42)
+license_files = LICENSE
\ No newline at end of file
diff --git a/setup.py b/setup.py
index d7a39cd..ea1e683 100644
--- a/setup.py
+++ b/setup.py
@@ -1,15 +1,48 @@
 from setuptools import setup, find_packages
-setup(
-    name = 'pyqn',
-    version = '1.2.2',
-    packages = find_packages(),
-    author = 'Christian Hill',
-    url = 'https://github.com/xnx/pyqn',
-    license = 'GPL',
-    author_email = 'christian.hill@ucl.ac.uk',
-
+from pathlib import Path
 
-    description = 'A package for managing physical units and quantities',
-)
+root = Path(__file__).parent.resolve()
 
+# Get the long description from the README file
+long_description = (root / "README.rst").read_text(encoding="utf-8")
 
+setup(
+    name="pyqn",
+    version="1.3",
+    description="A package for managing physical units and quantities",
+    long_description=long_description,
+    long_description_content_type="text/x-rst",
+    url="https://github.com/xnx/pyqn",
+    author="Christian Hill",
+    author_email="xn.hill@gmail.com",
+    classifiers=[
+        "Development Status :: 4 - Beta",
+        "Intended Audience :: Science/Research",
+        "Topic :: Scientific/Engineering :: Chemistry",
+        "Topic :: Scientific/Engineering :: Physics",
+        "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
+        "Programming Language :: Python :: 3",
+        "Programming Language :: Python :: 3.6",
+        "Programming Language :: Python :: 3.7",
+        "Programming Language :: Python :: 3.8",
+        "Programming Language :: Python :: 3.9",
+        "Programming Language :: Python :: 3.10",
+        "Programming Language :: Python :: 3 :: Only",
+        "Operating System :: OS Independent",
+    ],
+    keywords="chemistry, units, physical quantities, unit conversion",
+    package_dir={"": "src"},
+    packages=find_packages(where="src"),
+    python_requires=">=3.6",
+    install_requires=[
+        "pyparsing>=2.3",
+        'importlib-resources>=1.0; python_version < "3.7.0"',
+    ],
+    extras_require={"dev": ["black", "pytest-cov", "tox", "ipython"]},
+    # package_data will include all the resolved globs into both the wheel and sdist
+    #package_data={},
+    # no need for MANIFEST.in, which should be reserved only for build-time files
+    project_urls={
+        "Bug Reports": "https://github.com/xnx/pyqn/issues",
+    },
+)
diff --git a/pyqn/__init__.py b/src/pyqn/__init__.py
similarity index 100%
rename from pyqn/__init__.py
rename to src/pyqn/__init__.py
diff --git a/pyqn/atom_unit.py b/src/pyqn/atom_unit.py
similarity index 100%
rename from pyqn/atom_unit.py
rename to src/pyqn/atom_unit.py
diff --git a/pyqn/base_unit.py b/src/pyqn/base_unit.py
similarity index 99%
rename from pyqn/base_unit.py
rename to src/pyqn/base_unit.py
index 3fee18e..833c4a7 100644
--- a/pyqn/base_unit.py
+++ b/src/pyqn/base_unit.py
@@ -111,7 +111,7 @@ def __str__(self):
 BaseUnit('k', 'kayser', 'wavenumber', 100., '', 'k', d_length**-1),
 BaseUnit('D', 'debye', 'electric dipole moment', 1.e-21/299792458., '', 'D',
          d_charge * d_length),
-BaseUnit('hbar', 'hbar', 'angular momentum', 1.05457148e-34, '', '\hbar',
+BaseUnit('hbar', 'hbar', 'angular momentum', 1.05457148e-34, '', r'\hbar',
          Dimensions(L=2, M=1, T=-1)),
 BaseUnit('e', 'electron charge', 'charge', 1.602176565e-19, '', 'e', d_charge),
 ]),
@@ -144,7 +144,7 @@ def __str__(self):
 
 ('Non-SI units of length, area and volume', [
 # Non-SI length units
-BaseUnit('Å', 'angstrom', 'length', 1.e-10, '', '\AA', d_length),
+BaseUnit('Å', 'angstrom', 'length', 1.e-10, '', r'\AA', d_length),
 BaseUnit('a0', 'bohr', 'length', 5.2917721092e-11, '', 'a_0', d_length),
 # Non-SI area units
 BaseUnit('b', 'barn', 'area', 1.e-28, '', 'b', d_area),
diff --git a/pyqn/dimensions.py b/src/pyqn/dimensions.py
similarity index 100%
rename from pyqn/dimensions.py
rename to src/pyqn/dimensions.py
diff --git a/pyqn/list_base_units.py b/src/pyqn/list_base_units.py
similarity index 100%
rename from pyqn/list_base_units.py
rename to src/pyqn/list_base_units.py
diff --git a/pyqn/qn_array.py b/src/pyqn/qn_array.py
similarity index 100%
rename from pyqn/qn_array.py
rename to src/pyqn/qn_array.py
diff --git a/pyqn/quantity.py b/src/pyqn/quantity.py
similarity index 93%
rename from pyqn/quantity.py
rename to src/pyqn/quantity.py
index 61d38ce..077a1ac 100644
--- a/pyqn/quantity.py
+++ b/src/pyqn/quantity.py
@@ -22,7 +22,7 @@
 
 import re
 import math
-import numpy as np
+#import numpy as np
 from .symbol import Symbol
 from .units import Units, UnitsError
 
@@ -167,21 +167,21 @@ def convert_units_to(self, new_units, force=None):
         else:
             return Quantity(value = self.value*fac, units = new_units)
 
-    def draw_from_dist(self, shape=None):
-        """
-        Return a value or number array of values drawn from the normal
-        distribution described by this Quantity's mean and standard
-        deviation. shape is the shape of the NumPy array to return, or
-        None (the default) to return a single scalar value from the
-        distribution.
-
-        """
-
-        if self.sd is None:
-            raise ValueError('Quantity instance {} has no defined standard'
-                             ' deviation.'.format(self.name))
-
-        return np.random.normal(loc=self.value, scale=self.sd, size=shape)
+#    def draw_from_dist(self, shape=None):
+#        """
+#        Return a value or number array of values drawn from the normal
+#        distribution described by this Quantity's mean and standard
+#        deviation. shape is the shape of the NumPy array to return, or
+#        None (the default) to return a single scalar value from the
+#        distribution.
+#
+#        """
+#
+#        if self.sd is None:
+#            raise ValueError('Quantity instance {} has no defined standard'
+#                             ' deviation.'.format(self.name))
+#
+#        return np.random.normal(loc=self.value, scale=self.sd, size=shape)
 
     def __add__(self, other):
         """
@@ -311,7 +311,7 @@ def parse(self, s_quantity, name=None, units=None, sd=None,
         else:
             s_mantsd = s_valsd
             exp = 0
-        patt = '([+-]?\d*\.?\d*)\(?(\d+)?\)?'
+        patt = r'([+-]?\d*\.?\d*)\(?(\d+)?\)?'
         m = re.match(patt, s_mantsd)
         if not m:
             raise QuantityError('Failed to parse string into quantity:\n'\
diff --git a/pyqn/si.py b/src/pyqn/si.py
similarity index 100%
rename from pyqn/si.py
rename to src/pyqn/si.py
diff --git a/pyqn/symbol.py b/src/pyqn/symbol.py
similarity index 100%
rename from pyqn/symbol.py
rename to src/pyqn/symbol.py
diff --git a/pyqn/units.py b/src/pyqn/units.py
similarity index 100%
rename from pyqn/units.py
rename to src/pyqn/units.py
diff --git a/pyqn/tests/test_conversions.py b/tests/test_conversions.py
similarity index 97%
rename from pyqn/tests/test_conversions.py
rename to tests/test_conversions.py
index 558cf32..2c91aa0 100644
--- a/pyqn/tests/test_conversions.py
+++ b/tests/test_conversions.py
@@ -6,7 +6,7 @@
 # Unit tests for unit conversions within the Units class.
 
 import unittest
-from ..units import Units, UnitsError
+from pyqn.units import Units, UnitsError
 
 class UnitsConversionCheck(unittest.TestCase):
     """Unit tests for unit conversions within the Units class."""
diff --git a/pyqn/tests/test_quantity.py b/tests/test_quantity.py
similarity index 94%
rename from pyqn/tests/test_quantity.py
rename to tests/test_quantity.py
index c0111b4..f1864f9 100644
--- a/pyqn/tests/test_quantity.py
+++ b/tests/test_quantity.py
@@ -1,7 +1,7 @@
 import unittest
-from ..quantity import Quantity, QuantityError
-from ..dimensions import Dimensions, d_energy
-from ..units import UnitsError
+from pyqn.quantity import Quantity, QuantityError
+from pyqn.dimensions import Dimensions, d_energy
+from pyqn.units import UnitsError
 
 class QuantityManipulations(unittest.TestCase):
     def test_quantity_init(self):
@@ -77,4 +77,4 @@ def test_quantity_conversion(self):
         pass
         
 if __name__ == '__main__':
-    unittest.main()
\ No newline at end of file
+    unittest.main()
diff --git a/pyqn/tests/test_units.py b/tests/test_units.py
similarity index 97%
rename from pyqn/tests/test_units.py
rename to tests/test_units.py
index 26cb7bc..1e80d35 100644
--- a/pyqn/tests/test_units.py
+++ b/tests/test_units.py
@@ -6,8 +6,8 @@
 # Unit tests for the Units class.
 
 import unittest
-from ..units import Units
-from ..dimensions import Dimensions,d_energy
+from pyqn.units import Units
+from pyqn.dimensions import Dimensions,d_energy
 
 class UnitsCheck(unittest.TestCase):
     """Unit tests for the Units class."""
diff --git a/pyqn/tests/test_units_collisions.py b/tests/test_units_collisions.py
similarity index 90%
rename from pyqn/tests/test_units_collisions.py
rename to tests/test_units_collisions.py
index c52f6b6..f198d86 100644
--- a/pyqn/tests/test_units_collisions.py
+++ b/tests/test_units_collisions.py
@@ -8,9 +8,9 @@
 # of "min" for minutes.
 
 import unittest
-from ..units import Units
-from ..base_unit import base_units
-from ..si import si_prefixes
+from pyqn.units import Units
+from pyqn.base_unit import base_units
+from pyqn.si import si_prefixes
 
 class ConflictsCheck(unittest.TestCase):
     """