From 2ce8fe841793f43dbc45c28dcd87fcda5381f9aa Mon Sep 17 00:00:00 2001 From: Jason Thomale Date: Fri, 4 Nov 2022 13:54:47 -0500 Subject: [PATCH 01/12] Modernize project structure, configuration Changed a bunch of files here, but it's mostly just moving things around in an effort to modernize the project structure and implement a pyproject.toml for project configuration. - Moved all source files into a /src/pycallnumber directory. - Changed to absolute imports for all pycallnumber import statements. - Removed unnecessary `contexts.py` file from tests. Going forward we'll rely on installing the project locally using `pip install -e .[dev]`. Tests will run against that version. - Changed filename of LICENSE.txt to LICENSE. - Added `pyproject.toml` file and removed all other (now) unnecessary cfg files: setup.cfg, setup.py, MANIFEST.in. - The pyproject.toml file is now the single source of truth for project metadata. --- .gitignore | 12 ++-- LICENSE.txt => LICENSE | 0 MANIFEST.in | 3 - pycallnumber/__init__.py | 39 ------------- pycallnumber/units/__init__.py | 17 ------ pycallnumber/units/callnumbers/__init__.py | 10 ---- pyproject.toml | 55 +++++++++++++++++++ setup.cfg | 8 --- setup.py | 44 --------------- src/pycallnumber/__init__.py | 45 +++++++++++++++ .../pycallnumber}/exceptions.py | 0 .../pycallnumber}/factories.py | 6 +- {pycallnumber => src/pycallnumber}/options.py | 2 +- {pycallnumber => src/pycallnumber}/set.py | 8 +-- .../pycallnumber}/settings.py | 0 .../pycallnumber}/template.py | 9 ++- {pycallnumber => src/pycallnumber}/unit.py | 8 +-- src/pycallnumber/units/__init__.py | 18 ++++++ .../units/callnumbers/__init__.py | 10 ++++ .../pycallnumber}/units/callnumbers/dewey.py | 2 +- .../pycallnumber}/units/callnumbers/lc.py | 2 +- .../pycallnumber}/units/callnumbers/local.py | 0 .../pycallnumber}/units/callnumbers/parts.py | 0 .../pycallnumber}/units/callnumbers/sudoc.py | 2 +- .../pycallnumber}/units/compound.py | 3 +- .../pycallnumber}/units/dates/__init__.py | 2 +- .../pycallnumber}/units/dates/base.py | 0 .../pycallnumber}/units/dates/datestring.py | 4 +- .../pycallnumber}/units/dates/parts.py | 3 +- .../pycallnumber}/units/numbers.py | 4 +- .../pycallnumber}/units/simple.py | 0 {pycallnumber => src/pycallnumber}/utils.py | 2 +- tests/context.py | 25 --------- tests/test_factories.py | 14 ++--- tests/test_options.py | 2 +- tests/test_set.py | 12 ++-- tests/test_template.py | 6 +- tests/test_unit.py | 6 +- tests/test_units.py | 10 ++-- tests/test_utils.py | 4 +- 40 files changed, 190 insertions(+), 207 deletions(-) rename LICENSE.txt => LICENSE (100%) delete mode 100644 MANIFEST.in delete mode 100644 pycallnumber/__init__.py delete mode 100644 pycallnumber/units/__init__.py delete mode 100644 pycallnumber/units/callnumbers/__init__.py create mode 100644 pyproject.toml delete mode 100644 setup.cfg delete mode 100644 setup.py create mode 100644 src/pycallnumber/__init__.py rename {pycallnumber => src/pycallnumber}/exceptions.py (100%) rename {pycallnumber => src/pycallnumber}/factories.py (97%) rename {pycallnumber => src/pycallnumber}/options.py (98%) rename {pycallnumber => src/pycallnumber}/set.py (99%) rename {pycallnumber => src/pycallnumber}/settings.py (100%) rename {pycallnumber => src/pycallnumber}/template.py (99%) rename {pycallnumber => src/pycallnumber}/unit.py (96%) create mode 100644 src/pycallnumber/units/__init__.py create mode 100644 src/pycallnumber/units/callnumbers/__init__.py rename {pycallnumber => src/pycallnumber}/units/callnumbers/dewey.py (96%) rename {pycallnumber => src/pycallnumber}/units/callnumbers/lc.py (97%) rename {pycallnumber => src/pycallnumber}/units/callnumbers/local.py (100%) rename {pycallnumber => src/pycallnumber}/units/callnumbers/parts.py (100%) rename {pycallnumber => src/pycallnumber}/units/callnumbers/sudoc.py (99%) rename {pycallnumber => src/pycallnumber}/units/compound.py (92%) rename {pycallnumber => src/pycallnumber}/units/dates/__init__.py (65%) rename {pycallnumber => src/pycallnumber}/units/dates/base.py (100%) rename {pycallnumber => src/pycallnumber}/units/dates/datestring.py (96%) rename {pycallnumber => src/pycallnumber}/units/dates/parts.py (97%) rename {pycallnumber => src/pycallnumber}/units/numbers.py (98%) rename {pycallnumber => src/pycallnumber}/units/simple.py (100%) rename {pycallnumber => src/pycallnumber}/utils.py (99%) delete mode 100644 tests/context.py diff --git a/.gitignore b/.gitignore index 7a60b87..b08889b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,10 @@ *.pyc -/dist/ -/build/ -/*.egg-info -/*.egg +dist/ +build/ +*.egg-info +*.egg .cache/ __pycache__/ .python-version -/.eggs/ -/.tox/ \ No newline at end of file +.eggs/ +.tox/ diff --git a/LICENSE.txt b/LICENSE similarity index 100% rename from LICENSE.txt rename to LICENSE diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index babd07a..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,3 +0,0 @@ -include LICENSE.txt README.md pytest.ini tox.ini -recursive-include tests * -global-exclude *.py[co] diff --git a/pycallnumber/__init__.py b/pycallnumber/__init__.py deleted file mode 100644 index 650dfc4..0000000 --- a/pycallnumber/__init__.py +++ /dev/null @@ -1,39 +0,0 @@ -"""The pycallnumber top-level package. - -This package allows you to work with call numbers (Library of Congress, -Dewey Decimal, US SuDocs, and others)--parse them, normalize them, sort -them. -""" - -from __future__ import absolute_import - -from . import settings -from .exceptions import CallNumberError, CallNumberWarning,\ - InvalidCallNumberStringError, SettingsError,\ - MethodError, OptionsError, UtilsError, RangeSetError,\ - BadRange -from .options import Options, ObjectWithOptions -from .template import Template, SimpleTemplate, CompoundTemplate, Grouping -from .unit import Unit, SimpleUnit, CompoundUnit -from .set import RangeSet -from . import units -from . import utils -from .factories import callnumber, cnrange, cnset - -__all__ = ['settings', 'CallNumberError', 'CallNumberWarning', - 'InvalidCallNumberStringError', 'SettingsError', 'MethodError', - 'OptionsError', 'UtilsError', 'RangeSetError', 'BadRange', - 'Options', 'ObjectWithOptions', 'Template', 'SimpleTemplate', - 'CompoundTemplate', 'Grouping', 'Unit', 'SimpleUnit', - 'CompoundUnit', 'RangeSet', 'units', - 'utils', 'callnumber', 'cnrange', 'cnset'] - -__version__ = '0.1.4' -__name__ = 'pycallnumber' -__url__ = 'https://github.com/unt-libraries/pycallnumber' -__description__ = 'A Python library for parsing call numbers.' -__license__ = 'BSD' -__author__ = 'Jason Thomale' -__author_email__ = 'jason.thomale@unt.edu' -__maintainer__ = 'University of North Texas Libraries' -__keywords__ = 'python, callnumber, callnumbers, call number, call numbers' diff --git a/pycallnumber/units/__init__.py b/pycallnumber/units/__init__.py deleted file mode 100644 index bdf10f9..0000000 --- a/pycallnumber/units/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Work with predefined types of call numbers and call number parts.""" -from __future__ import absolute_import - -from .simple import Alphabetic, Numeric, Formatting -from .compound import AlphaNumeric, AlphaSymbol, NumericSymbol,\ - AlphaNumericSymbol -from .numbers import Number, OrdinalNumber -from .dates import DateString -from .callnumbers.parts import Cutter, Edition, Item -from .callnumbers import LC, LcClass, Dewey, DeweyClass, SuDoc, Agency,\ - AgencyDotSeries, Local - -__all__ = ['Alphabetic', 'Numeric', 'Formatting', 'AlphaNumeric', - 'AlphaSymbol', 'NumericSymbol', 'AlphaNumericSymbol', 'Number', - 'OrdinalNumber', 'DateString', 'Cutter', 'Edition', 'Item', 'LC', - 'LcClass', 'Dewey', 'DeweyClass', 'SuDoc', 'Agency', - 'AgencyDotSeries', 'Local'] diff --git a/pycallnumber/units/callnumbers/__init__.py b/pycallnumber/units/callnumbers/__init__.py deleted file mode 100644 index eeb7b35..0000000 --- a/pycallnumber/units/callnumbers/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -"""Work with standard call number types.""" -from __future__ import absolute_import - -from .dewey import Dewey, DeweyClass -from .lc import LC, LcClass -from .sudoc import SuDoc, Agency, AgencyDotSeries -from .local import Local - -__all__ = ['Dewey', 'DeweyClass', 'LC', 'LcClass', 'SuDoc', 'Agency', - 'AgencyDotSeries', 'Local'] diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..1985f5c --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,55 @@ +[build-system] +requires = ["setuptools>=64.0.0", "wheel", "setuptools_scm[toml]>=6.2"] +build-backend = "setuptools.build_meta" + +[project] +name = "pycallnumber" +description = "A Python library for parsing call numbers." +readme = "README.md" +authors = [{ name = "Jason Thomale", email = "jason.thomale@unt.edu" }] +maintainers = [{ name = "University of North Texas Libraries" }] +keywords = ["python", "callnumber", "callnumbers", "call number", + "call numbers"] +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Education", + "Intended Audience :: Developers", + "Natural Language :: English", + "License :: OSI Approved :: BSD License", + "Programming Language :: Python", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.4", + "Programming Language :: Python :: 3.5", + "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.11", + "Topic :: Software Development", +] +dynamic = ["version"] +requires-python = ">=3.7" +dependencies = [ + 'future; python_version=="2.7"', + # For Python >=3.8 we use importlib.metadata to get the installed + # package version so we can use pyproject.toml as the single source + # of truth for the version number. This was new in 3.8, so for 3.7 + # we have to use importlib_metadata. + 'importlib_metadata >= 2.0.0; python_version == "3.7"' +] + +[project.urls] +homepage = "https://github.com/unt-libraries/pycallnumber" +repository = "https://github.com/unt-libraries/pycallnumber" + +[project.optional-dependencies] +dev = [ + 'pytest >= 6.2.4; python_version >= "3.10"', + 'pytest >= 3.0.0; python_version < "3.10"' +] + +[tool.setuptools_scm] +local_scheme = "no-local-version" diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index a1fabe0..0000000 --- a/setup.cfg +++ /dev/null @@ -1,8 +0,0 @@ -[aliases] -test=pytest - -[metadata] -description-file = README.md - -[wheel] -universal = 1 diff --git a/setup.py b/setup.py deleted file mode 100644 index 53fddc8..0000000 --- a/setup.py +++ /dev/null @@ -1,44 +0,0 @@ -#! /usr/bin/env python - -import os -import setuptools - -meta = {} -with open(os.path.join('pycallnumber', '__init__.py')) as fh: - variables = [l.split(' = ') for l in fh if l.startswith('__')] - for var in variables: - meta[var[0].strip('_')] = var[-1].strip('"\'\n') - -setuptools.setup( - name=meta['name'], - author=meta['author'], - author_email=meta['author_email'], - version=meta['version'], - url=meta['url'], - license=meta['license'], - description=meta['description'], - long_description=('Visit {} for the latest documentation.'.format( - meta['url'])), - maintainer=meta['maintainer'], - keywords=meta['keywords'], - packages=setuptools.find_packages(), - install_requires=[ - 'future;python_version=="2.7"' - ], - setup_requires=[ - 'pytest-runner' - ], - tests_require=[ - 'pytest' - ], - classifiers=[ - 'Intended Audience :: Education', - 'Natural Language :: English', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - ] -) diff --git a/src/pycallnumber/__init__.py b/src/pycallnumber/__init__.py new file mode 100644 index 0000000..6876d9f --- /dev/null +++ b/src/pycallnumber/__init__.py @@ -0,0 +1,45 @@ +"""The pycallnumber top-level package. + +This package allows you to work with call numbers (Library of Congress, +Dewey Decimal, US SuDocs, and others)--parse them, normalize them, sort +them. +""" + +from __future__ import absolute_import + +try: + from importlib import metadata +except (ImportError, ModuleNotFoundError): + import importlib_metadata as metadata + +from pycallnumber import settings +from pycallnumber.exceptions import CallNumberError, CallNumberWarning,\ + InvalidCallNumberStringError,\ + SettingsError, MethodError, OptionsError,\ + UtilsError, RangeSetError, BadRange +from pycallnumber.options import Options, ObjectWithOptions +from pycallnumber.template import Template, SimpleTemplate, CompoundTemplate,\ + Grouping +from pycallnumber.unit import Unit, SimpleUnit, CompoundUnit +from pycallnumber.set import RangeSet +from pycallnumber import units +from pycallnumber import utils +from pycallnumber.factories import callnumber, cnrange, cnset + +_md = metadata.metadata('pycallnumber') +__version__ = metadata.version('pycallnumber') +__name__ = 'pycallnumber' +__url__ = _md['Project-url'].split(', ')[1] +__description__ = _md['Summary'] +__license__ = 'BSD' +__author__ = _md['Author-email'].split(' <')[0] +__author_email__ = _md['Author-email'].split(' <')[1].rstrip('>') +__maintainer__ = _md['Maintainer'] +__keywords__ = _md['Keywords'] +__all__ = ['settings', 'CallNumberError', 'CallNumberWarning', + 'InvalidCallNumberStringError', 'SettingsError', 'MethodError', + 'OptionsError', 'UtilsError', 'RangeSetError', 'BadRange', + 'Options', 'ObjectWithOptions', 'Template', 'SimpleTemplate', + 'CompoundTemplate', 'Grouping', 'Unit', 'SimpleUnit', + 'CompoundUnit', 'RangeSet', 'units', + 'utils', 'callnumber', 'cnrange', 'cnset'] diff --git a/pycallnumber/exceptions.py b/src/pycallnumber/exceptions.py similarity index 100% rename from pycallnumber/exceptions.py rename to src/pycallnumber/exceptions.py diff --git a/pycallnumber/factories.py b/src/pycallnumber/factories.py similarity index 97% rename from pycallnumber/factories.py rename to src/pycallnumber/factories.py index 3d780c4..eeeb160 100644 --- a/pycallnumber/factories.py +++ b/src/pycallnumber/factories.py @@ -1,9 +1,9 @@ """Use factories to generate call number units and ranges.""" from __future__ import absolute_import -from . import settings -from .utils import create_unit, load_class -from .exceptions import InvalidCallNumberStringError, SettingsError +from pycallnumber import settings +from pycallnumber.utils import create_unit, load_class +from pycallnumber.exceptions import InvalidCallNumberStringError, SettingsError def callnumber(cnstr, name='', useropts=None, unittypes=None): diff --git a/pycallnumber/options.py b/src/pycallnumber/options.py similarity index 98% rename from pycallnumber/options.py rename to src/pycallnumber/options.py index 60ba397..34115a6 100644 --- a/pycallnumber/options.py +++ b/src/pycallnumber/options.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals from __future__ import absolute_import from builtins import object -from .exceptions import OptionsError +from pycallnumber.exceptions import OptionsError class Options(dict): diff --git a/pycallnumber/set.py b/src/pycallnumber/set.py similarity index 99% rename from pycallnumber/set.py rename to src/pycallnumber/set.py index 4a6eea3..4075a5b 100644 --- a/pycallnumber/set.py +++ b/src/pycallnumber/set.py @@ -6,12 +6,12 @@ from builtins import object import operator import copy - -from .exceptions import RangeSetError, BadRange -from .unit import Unit -from . import utils as u from functools import reduce +from pycallnumber.exceptions import RangeSetError, BadRange +from pycallnumber.unit import Unit +from pycallnumber import utils as u + class NonDiscreteSet(object): diff --git a/pycallnumber/settings.py b/src/pycallnumber/settings.py similarity index 100% rename from pycallnumber/settings.py rename to src/pycallnumber/settings.py diff --git a/pycallnumber/template.py b/src/pycallnumber/template.py similarity index 99% rename from pycallnumber/template.py rename to src/pycallnumber/template.py index 3635cf9..ab7cd5a 100644 --- a/pycallnumber/template.py +++ b/src/pycallnumber/template.py @@ -1,16 +1,15 @@ """Implement the patterns that Unit objects use.""" - from __future__ import unicode_literals from __future__ import absolute_import from builtins import str import re import collections -from .options import ObjectWithOptions -from .exceptions import InvalidCallNumberStringError, SettingsError,\ - MethodError -from . import utils as u +from pycallnumber.options import ObjectWithOptions +from pycallnumber.exceptions import InvalidCallNumberStringError,\ + SettingsError, MethodError +from pycallnumber import utils as u class Template(ObjectWithOptions): diff --git a/pycallnumber/unit.py b/src/pycallnumber/unit.py similarity index 96% rename from pycallnumber/unit.py rename to src/pycallnumber/unit.py index a4bef70..3a3b712 100644 --- a/pycallnumber/unit.py +++ b/src/pycallnumber/unit.py @@ -5,10 +5,10 @@ from __future__ import absolute_import import inspect -from .options import ObjectWithOptions -from .exceptions import InvalidCallNumberStringError -from .template import Template, SimpleTemplate, CompoundTemplate -from . import utils as u +from pycallnumber.options import ObjectWithOptions +from pycallnumber.exceptions import InvalidCallNumberStringError +from pycallnumber.template import Template, SimpleTemplate, CompoundTemplate +from pycallnumber import utils as u class Unit(u.ComparableObjectMixin, ObjectWithOptions): diff --git a/src/pycallnumber/units/__init__.py b/src/pycallnumber/units/__init__.py new file mode 100644 index 0000000..9b3a8a7 --- /dev/null +++ b/src/pycallnumber/units/__init__.py @@ -0,0 +1,18 @@ +"""Work with predefined types of call numbers and call number parts.""" +from __future__ import absolute_import + +from pycallnumber.units.simple import Alphabetic, Numeric, Formatting +from pycallnumber.units.compound import AlphaNumeric, AlphaSymbol,\ + NumericSymbol, AlphaNumericSymbol +from pycallnumber.units.numbers import Number, OrdinalNumber +from pycallnumber.units.dates import DateString +from pycallnumber.units.callnumbers.parts import Cutter, Edition, Item +from pycallnumber.units.callnumbers import LC, LcClass, Dewey, DeweyClass,\ + SuDoc, Agency, AgencyDotSeries,\ + Local + +__all__ = ['Alphabetic', 'Numeric', 'Formatting', 'AlphaNumeric', + 'AlphaSymbol', 'NumericSymbol', 'AlphaNumericSymbol', 'Number', + 'OrdinalNumber', 'DateString', 'Cutter', 'Edition', 'Item', 'LC', + 'LcClass', 'Dewey', 'DeweyClass', 'SuDoc', 'Agency', + 'AgencyDotSeries', 'Local'] diff --git a/src/pycallnumber/units/callnumbers/__init__.py b/src/pycallnumber/units/callnumbers/__init__.py new file mode 100644 index 0000000..27c5de5 --- /dev/null +++ b/src/pycallnumber/units/callnumbers/__init__.py @@ -0,0 +1,10 @@ +"""Work with standard call number types.""" +from __future__ import absolute_import + +from pycallnumber.units.callnumbers.dewey import Dewey, DeweyClass +from pycallnumber.units.callnumbers.lc import LC, LcClass +from pycallnumber.units.callnumbers.sudoc import SuDoc, Agency, AgencyDotSeries +from pycallnumber.units.callnumbers.local import Local + +__all__ = ['Dewey', 'DeweyClass', 'LC', 'LcClass', 'SuDoc', 'Agency', + 'AgencyDotSeries', 'Local'] diff --git a/pycallnumber/units/callnumbers/dewey.py b/src/pycallnumber/units/callnumbers/dewey.py similarity index 96% rename from pycallnumber/units/callnumbers/dewey.py rename to src/pycallnumber/units/callnumbers/dewey.py index 9c80310..3150ce6 100644 --- a/pycallnumber/units/callnumbers/dewey.py +++ b/src/pycallnumber/units/callnumbers/dewey.py @@ -7,7 +7,7 @@ from pycallnumber.units.simple import Alphabetic, DEFAULT_SEPARATOR_TYPE from pycallnumber.units.compound import AlphaNumericSymbol from pycallnumber.units.numbers import Number -from .parts import Cutter, Edition, Item +from pycallnumber.units.callnumbers.parts import Cutter, Edition, Item DeweyClass = Number.derive( diff --git a/pycallnumber/units/callnumbers/lc.py b/src/pycallnumber/units/callnumbers/lc.py similarity index 97% rename from pycallnumber/units/callnumbers/lc.py rename to src/pycallnumber/units/callnumbers/lc.py index d803bad..63acd4c 100644 --- a/pycallnumber/units/callnumbers/lc.py +++ b/src/pycallnumber/units/callnumbers/lc.py @@ -8,7 +8,7 @@ DEFAULT_SEPARATOR_TYPE from pycallnumber.units.compound import AlphaNumericSymbol from pycallnumber.units.numbers import Number -from .parts import Cutter, Edition, Item +from pycallnumber.units.callnumbers.parts import Cutter, Edition, Item class LcClass(AlphaNumericSymbol): diff --git a/pycallnumber/units/callnumbers/local.py b/src/pycallnumber/units/callnumbers/local.py similarity index 100% rename from pycallnumber/units/callnumbers/local.py rename to src/pycallnumber/units/callnumbers/local.py diff --git a/pycallnumber/units/callnumbers/parts.py b/src/pycallnumber/units/callnumbers/parts.py similarity index 100% rename from pycallnumber/units/callnumbers/parts.py rename to src/pycallnumber/units/callnumbers/parts.py diff --git a/pycallnumber/units/callnumbers/sudoc.py b/src/pycallnumber/units/callnumbers/sudoc.py similarity index 99% rename from pycallnumber/units/callnumbers/sudoc.py rename to src/pycallnumber/units/callnumbers/sudoc.py index 50a029b..50a84f7 100644 --- a/pycallnumber/units/callnumbers/sudoc.py +++ b/src/pycallnumber/units/callnumbers/sudoc.py @@ -8,7 +8,7 @@ from pycallnumber.units.simple import Alphabetic, Numeric, Formatting,\ DEFAULT_SEPARATOR_TYPE from pycallnumber.units.compound import AlphaNumericSymbol -from .parts import Cutter +from pycallnumber.units.callnumbers.parts import Cutter # Various base SimpleUnits for SuDoc component classes diff --git a/pycallnumber/units/compound.py b/src/pycallnumber/units/compound.py similarity index 92% rename from pycallnumber/units/compound.py rename to src/pycallnumber/units/compound.py index 848d9eb..4187bf6 100644 --- a/pycallnumber/units/compound.py +++ b/src/pycallnumber/units/compound.py @@ -6,7 +6,8 @@ from pycallnumber.template import CompoundTemplate from pycallnumber.unit import CompoundUnit -from .simple import Alphabetic, Numeric, Formatting, DEFAULT_SEPARATOR_TYPE +from pycallnumber.units.simple import Alphabetic, Numeric, Formatting,\ + DEFAULT_SEPARATOR_TYPE class AlphaNumeric(CompoundUnit): diff --git a/pycallnumber/units/dates/__init__.py b/src/pycallnumber/units/dates/__init__.py similarity index 65% rename from pycallnumber/units/dates/__init__.py rename to src/pycallnumber/units/dates/__init__.py index 25958ea..bb3dce4 100644 --- a/pycallnumber/units/dates/__init__.py +++ b/src/pycallnumber/units/dates/__init__.py @@ -1,6 +1,6 @@ """Parse date strings as call number parts.""" from __future__ import absolute_import -from .datestring import DateString +from pycallnumber.units.dates.datestring import DateString __all__ = ['DateString'] diff --git a/pycallnumber/units/dates/base.py b/src/pycallnumber/units/dates/base.py similarity index 100% rename from pycallnumber/units/dates/base.py rename to src/pycallnumber/units/dates/base.py diff --git a/pycallnumber/units/dates/datestring.py b/src/pycallnumber/units/dates/datestring.py similarity index 96% rename from pycallnumber/units/dates/datestring.py rename to src/pycallnumber/units/dates/datestring.py index 5c8b251..a778f74 100644 --- a/pycallnumber/units/dates/datestring.py +++ b/src/pycallnumber/units/dates/datestring.py @@ -6,8 +6,8 @@ from pycallnumber.template import CompoundTemplate from pycallnumber.units.simple import Formatting, DEFAULT_SEPARATOR_TYPE -from .base import BaseDate -from .parts import Year, Month, Day +from pycallnumber.units.dates.base import BaseDate +from pycallnumber.units.dates.parts import Year, Month, Day Separator = Formatting.derive( diff --git a/pycallnumber/units/dates/parts.py b/src/pycallnumber/units/dates/parts.py similarity index 97% rename from pycallnumber/units/dates/parts.py rename to src/pycallnumber/units/dates/parts.py index 9f0508d..87d8249 100644 --- a/pycallnumber/units/dates/parts.py +++ b/src/pycallnumber/units/dates/parts.py @@ -13,7 +13,8 @@ from pycallnumber.template import SimpleTemplate, CompoundTemplate from pycallnumber.units.simple import Formatting from pycallnumber.units.numbers import OrdinalNumber -from .base import AlphaDatePart, NumericDatePart, CompoundDatePart +from pycallnumber.units.dates.base import AlphaDatePart, NumericDatePart,\ + CompoundDatePart class Year(NumericDatePart): diff --git a/pycallnumber/units/numbers.py b/src/pycallnumber/units/numbers.py similarity index 98% rename from pycallnumber/units/numbers.py rename to src/pycallnumber/units/numbers.py index 71d197b..4043070 100644 --- a/pycallnumber/units/numbers.py +++ b/src/pycallnumber/units/numbers.py @@ -12,8 +12,8 @@ from pycallnumber.exceptions import InvalidCallNumberStringError, SettingsError from pycallnumber.template import CompoundTemplate import pycallnumber.utils as u -from .simple import Alphabetic, Numeric, Formatting -from .compound import AlphaNumericSymbol +from pycallnumber.units.simple import Alphabetic, Numeric, Formatting +from pycallnumber.units.compound import AlphaNumericSymbol ThreeDigits = Numeric.derive( diff --git a/pycallnumber/units/simple.py b/src/pycallnumber/units/simple.py similarity index 100% rename from pycallnumber/units/simple.py rename to src/pycallnumber/units/simple.py diff --git a/pycallnumber/utils.py b/src/pycallnumber/utils.py similarity index 99% rename from pycallnumber/utils.py rename to src/pycallnumber/utils.py index dfe5d8e..6e84f53 100644 --- a/pycallnumber/utils.py +++ b/src/pycallnumber/utils.py @@ -12,7 +12,7 @@ import importlib import types -from .exceptions import InvalidCallNumberStringError +from pycallnumber.exceptions import InvalidCallNumberStringError def memoize(function): diff --git a/tests/context.py b/tests/context.py deleted file mode 100644 index c435c7d..0000000 --- a/tests/context.py +++ /dev/null @@ -1,25 +0,0 @@ -"""Provides imports for all test scripts. - -This is used so that tests can be run without having to have the -pycallnumber package installed, from directories other than tests. -""" - -import os -import sys - - -current_path = os.path.dirname(os.path.realpath(__file__)) -pycn_path = os.path.abspath('{}/..'.format(current_path)) -sys.path.insert(0, pycn_path) - - -import pycallnumber -from pycallnumber import exceptions -from pycallnumber import factories -from pycallnumber import options -from pycallnumber import set -from pycallnumber import settings -from pycallnumber import template -from pycallnumber import unit -from pycallnumber import units -from pycallnumber import utils diff --git a/tests/test_factories.py b/tests/test_factories.py index ae40441..0222922 100644 --- a/tests/test_factories.py +++ b/tests/test_factories.py @@ -1,11 +1,11 @@ import pytest -from context import unit as un -from context import units as uns -from context import template as t -from context import exceptions as e -from context import set as s -from context import factories as f +from pycallnumber import unit as un +from pycallnumber import units as uns +from pycallnumber import template as t +from pycallnumber import exceptions as e +from pycallnumber import set as s +from pycallnumber import factories as f # Fixtures, factories, and test data @@ -54,7 +54,7 @@ class FactoryTestRangeSetType(s.RangeSet): def test_star_imports(): """Star imports should work without raising errors.""" - from context import pycallnumber + import pycallnumber all_imports = __import__('pycallnumber', globals(), locals(), ['*']) assert all_imports.callnumber assert len(all_imports.__all__) == len(pycallnumber.__all__) diff --git a/tests/test_options.py b/tests/test_options.py index 21ce0c8..258edf4 100644 --- a/tests/test_options.py +++ b/tests/test_options.py @@ -1,7 +1,7 @@ from __future__ import unicode_literals -from context import options +from pycallnumber import options # Fixtures, factories, and test data diff --git a/tests/test_set.py b/tests/test_set.py index 0fdff4b..562da1e 100644 --- a/tests/test_set.py +++ b/tests/test_set.py @@ -3,12 +3,12 @@ import pytest -from context import utils as u -from context import unit as un -from context import units as uns -from context import template as t -from context import exceptions as e -from context import set as s +from pycallnumber import utils as u +from pycallnumber import unit as un +from pycallnumber import units as uns +from pycallnumber import template as t +from pycallnumber import exceptions as e +from pycallnumber import set as s from helpers import generate_params, mark_params diff --git a/tests/test_template.py b/tests/test_template.py index 8b5153b..7f4ebd5 100644 --- a/tests/test_template.py +++ b/tests/test_template.py @@ -4,9 +4,9 @@ import pytest -from context import unit as u -from context import template as t -from context import exceptions as e +from pycallnumber import unit as u +from pycallnumber import template as t +from pycallnumber import exceptions as e from helpers import make_obj_factory, generate_params diff --git a/tests/test_unit.py b/tests/test_unit.py index 1589063..30e3057 100644 --- a/tests/test_unit.py +++ b/tests/test_unit.py @@ -4,9 +4,9 @@ import pytest -from context import unit as u -from context import template as t -from context import exceptions as e +from pycallnumber import unit as u +from pycallnumber import template as t +from pycallnumber import exceptions as e from helpers import generate_params diff --git a/tests/test_units.py b/tests/test_units.py index 6a0f634..7af8131 100644 --- a/tests/test_units.py +++ b/tests/test_units.py @@ -1,7 +1,7 @@ import pytest -from context import units as u -from context import exceptions as e +from pycallnumber import units as u +from pycallnumber import exceptions as e from helpers import generate_params @@ -718,7 +718,7 @@ def test_units_star_imports(): """Star imports for the ``units`` package should work without raising errors.""" - from context import pycallnumber + import pycallnumber all_imp = __import__('pycallnumber.units', globals(), locals(), ['*']) assert all_imp.Alphabetic assert len(all_imp.__all__) == len(pycallnumber.units.__all__) @@ -727,7 +727,7 @@ def test_units_star_imports(): def test_units_callnumbers_star_imports(): """Star imports for the ``units.callnumbers`` package should work without raising errors.""" - from context import pycallnumber + import pycallnumber all_imp = __import__('pycallnumber.units.callnumbers', globals(), locals(), ['*']) assert all_imp.LC @@ -737,7 +737,7 @@ def test_units_callnumbers_star_imports(): def test_units_dates_star_imports(): """Star imports for the ``units.dates`` package should work without raising errors.""" - from context import pycallnumber + import pycallnumber all_imp = __import__('pycallnumber.units.dates', globals(), locals(), ['*']) assert all_imp.DateString diff --git a/tests/test_utils.py b/tests/test_utils.py index 3fbadc7..0c2d02c 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -4,8 +4,8 @@ import pytest -from context import utils as u -from context import units as uns +from pycallnumber import utils as u +from pycallnumber import units as uns # Fixtures, factories, and test data From 56921d1cdcb37ce1b3533cce6bad600eecac2eb2 Mon Sep 17 00:00:00 2001 From: Jason Thomale Date: Fri, 4 Nov 2022 16:01:22 -0500 Subject: [PATCH 02/12] Move tox.ini into pyproject.toml and update Cribbed from the fauxdoc tox ini to set this up. Note that I've removed Python 3.4 from the list of tox envs: I'm no longer going to test it because I can't get it to compile on Ubuntu using openssl 1.1. --- pyproject.toml | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++ tox.ini | 10 -------- 2 files changed, 66 insertions(+), 10 deletions(-) delete mode 100644 tox.ini diff --git a/pyproject.toml b/pyproject.toml index 1985f5c..8de7b12 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -53,3 +53,69 @@ dev = [ [tool.setuptools_scm] local_scheme = "no-local-version" + +[tool.tox] +legacy_tox_ini = """ +[tox] +envlist = flake8,py{27,35,36,37,38,39,310,311}-{oldest,latest} +isolated_build = True + +[testenv] +extras = + dev +commands = + pytest + +[testenv:py27-oldest] +deps = + pytest==3.0.0 + importlib_metadata==2.0.0 + +[testenv:py35-oldest] +deps = + pytest==3.0.0 + importlib_metadata==2.0.0 + +[testenv:py36-oldest] +deps = + pytest==3.0.0 + importlib_metadata==2.0.0 + +[testenv:py37-oldest] +deps = + pytest==3.0.0 + importlib_metadata==2.0.0 + +[testenv:py{38,39}-oldest] +deps = + pytest==3.0.0 + +[testenv:py{310,311}-oldest] +deps = + pytest==6.2.4 + +[testenv:flake8] +basepython = python3.10 +skip_install = True +deps = + flake8 +commands = + flake8 src/pycallnumber tests --exclude=__pycache__ + +[testenv:build_package] +basepython = python3.10 +skip_install = True +deps = + pytest + build + twine +allowlist_externals = + bash +commands = + bash -c 'rm -rf dist' + python -m build + bash -c 'python -m twine check dist/*.whl' + bash -c 'python -m twine check dist/*.gz' + bash -c 'python -m pip install dist/*.whl' + pytest +""" diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 7bf0696..0000000 --- a/tox.ini +++ /dev/null @@ -1,10 +0,0 @@ -[tox] -envlist=py27,py34,py35,py36,py37,flake8 - -[testenv] -deps=pytest -commands=py.test {posargs} - -[testenv:flake8] -deps=flake8 -commands=flake8 --exclude=.git,__pycache__,.cache,.egg*,.tox,tests/context.py From 9fe9b75d914c7790cb73cbacc010daf3a515f427 Mon Sep 17 00:00:00 2001 From: Jason Thomale Date: Mon, 7 Nov 2022 13:50:27 -0600 Subject: [PATCH 03/12] Roll back some modernization steps In order to continue supporting older versions of Python (3.6 and below) using setuptools we have to roll back to a `setup.cfg` configuration. --- README.md | 64 ++++++++++++++++++++---------------- pyproject.toml | 61 ++++------------------------------ setup.cfg | 47 ++++++++++++++++++++++++++ setup.py | 3 ++ src/pycallnumber/__init__.py | 10 +++--- 5 files changed, 97 insertions(+), 88 deletions(-) create mode 100644 setup.cfg create mode 100644 setup.py diff --git a/README.md b/README.md index 9a0e9d0..88d072b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# pycallnumber [![Build Status](https://travis-ci.org/unt-libraries/pycallnumber.svg?branch=master)](https://travis-ci.org/unt-libraries/pycallnumber) +# pycallnumber Use pycallnumber in your library's Python projects to parse, model, and manipulate any type of call number string. Support for Library of Congress, Dewey Decimal, SuDocs, and local call numbers is built in, and you can extend built-in classes to customize behavior or model other types of call numbers and formatted strings. @@ -10,60 +10,68 @@ Use pycallnumber in your library's Python projects to parse, model, and manipula ### Requirements - * Python 2.7, 3.4, 3.5, 3.6, or 3.7 +Tests pass on Linux and MacOS Python 2.7, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10, and 3.11. Version 3.4 and below may still work, but I'm unable to get these to compile any more so cannot test them. + +#### Warning: Outdated Python Versions + +The next version of pycallnumber (likely 1.0.0) will drop support for Python versions older than 3.7. + +### Dependencies + +If you're using Python >=3.8, there are no external dependencies beyond the standard library. + +For Python 2.7 to 3.7, the `importlib_metadata` backport is used for `importlib.metadata` functionality (first available in Python 3.8). + +For Python 2.7, the `future` module is used to replicate various Python 3 behaviors. ### Setup -Installing to a [virtualenv](http://docs.python-guide.org/en/latest/dev/virtualenvs/) using pip is recommended. +Installing to a [virtualenv](https://docs.python-guide.org/en/latest/dev/virtualenvs/) using pip is recommended. ```sh -$ pip install -U pip # Do this if the install fails at first -$ pip install pycallnumber +$ python -m pip install pycallnumber ``` #### Development setup and testing -If you want to contribute to pycallnumber, you'll want to fork the project and then download and install your fork from GitHub. E.g.: +If you want to contribute to pycallnumber, you should fork the project and then download and install your fork from GitHub. E.g.: ```sh -$ git clone https://github.com/[your-github-user]/pycallnumber.git pycallnumber +git clone https://github.com/[your-github-user]/pycallnumber.git pycallnumber ``` or (SSH) ```sh -$ git clone git@github.com:[your-github-user]/pycallnumber.git pycallnumber +git clone git@github.com:[your-github-user]/pycallnumber.git pycallnumber ``` -```sh -$ pip install ./pycallnumber -``` -or, if you're updating to a newer version, -```sh -$ pip install --upgrade ./pycallnumber -``` +Then use pip to do an editable install of the package with the `dev` extras (which installs pytest). -If not using pip, you can run the setuptools install command instead: ```sh -$ cd pycallnumber -$ python setup.py install +cd pycallnumber +python -m pip install -e .[dev] ``` ##### Running tests -(The below commands assume you've installed from GitHub and are in the repository root.) +(The below commands assume you've installed from GitHub as described above and are in the repository root.) -You can use [pytest](http://doc.pytest.org/) to run tests in your current Python environment. +Invoke [pytest](http://doc.pytest.org/) to run tests in your current Python environment. ```sh -$ pip install pytest -$ py.test +pytest ``` -Or you can use [tox](https://tox.readthedocs.io/) to run tests against multiple Python versions. +##### Tox + +You can use [tox](https://tox.wiki/en/latest/) to run tests against multiple Python versions, provided you have them available on the `PATH`. An excellent tool for this is [pyenv](https://github.com/pyenv/pyenv) with [pyenv-virtualenv](https://github.com/pyenv/pyenv-virtualenv). + +The tox configuration is in `pyproject.toml` (see the `[tool.tox]` section), which defines several test environments. You can run them all at once or target specific environments. + ```sh -$ pip install tox -$ tox # run tests against all configured environments -$ tox -e py27 # run tests just against python 2.7 -$ tox -e py34 # run tests just against python 3.4 -etc. +tox # run tests against all configured environments +tox -e py27-oldest # run tests against python 2.7 with oldest deps +tox -e py310-latest # run tests against python 3.10 with latest deps +tox -e flake8 # run flake8 linting +# etc. ``` [Top](#top) diff --git a/pyproject.toml b/pyproject.toml index 8de7b12..53289e7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,56 +1,7 @@ [build-system] -requires = ["setuptools>=64.0.0", "wheel", "setuptools_scm[toml]>=6.2"] +requires = ['setuptools>=44.0.0', 'wheel', 'setuptools_scm[toml]>=5.0.2'] build-backend = "setuptools.build_meta" -[project] -name = "pycallnumber" -description = "A Python library for parsing call numbers." -readme = "README.md" -authors = [{ name = "Jason Thomale", email = "jason.thomale@unt.edu" }] -maintainers = [{ name = "University of North Texas Libraries" }] -keywords = ["python", "callnumber", "callnumbers", "call number", - "call numbers"] -classifiers = [ - "Development Status :: 5 - Production/Stable", - "Intended Audience :: Education", - "Intended Audience :: Developers", - "Natural Language :: English", - "License :: OSI Approved :: BSD License", - "Programming Language :: Python", - "Programming Language :: Python :: 2", - "Programming Language :: Python :: 2.7", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.4", - "Programming Language :: Python :: 3.5", - "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.11", - "Topic :: Software Development", -] -dynamic = ["version"] -requires-python = ">=3.7" -dependencies = [ - 'future; python_version=="2.7"', - # For Python >=3.8 we use importlib.metadata to get the installed - # package version so we can use pyproject.toml as the single source - # of truth for the version number. This was new in 3.8, so for 3.7 - # we have to use importlib_metadata. - 'importlib_metadata >= 2.0.0; python_version == "3.7"' -] - -[project.urls] -homepage = "https://github.com/unt-libraries/pycallnumber" -repository = "https://github.com/unt-libraries/pycallnumber" - -[project.optional-dependencies] -dev = [ - 'pytest >= 6.2.4; python_version >= "3.10"', - 'pytest >= 3.0.0; python_version < "3.10"' -] - [tool.setuptools_scm] local_scheme = "no-local-version" @@ -68,27 +19,27 @@ commands = [testenv:py27-oldest] deps = - pytest==3.0.0 + pytest==3.5.0 importlib_metadata==2.0.0 [testenv:py35-oldest] deps = - pytest==3.0.0 + pytest==3.5.0 importlib_metadata==2.0.0 [testenv:py36-oldest] deps = - pytest==3.0.0 + pytest==3.5.0 importlib_metadata==2.0.0 [testenv:py37-oldest] deps = - pytest==3.0.0 + pytest==3.5.0 importlib_metadata==2.0.0 [testenv:py{38,39}-oldest] deps = - pytest==3.0.0 + pytest==3.5.0 [testenv:py{310,311}-oldest] deps = diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..cd6de30 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,47 @@ +[metadata] +name=pycallnumber +version=attr: pycallnumber.__version__ +description=A Python library for parsing call numbers. +long_description=file: README.md +long_description_content_type=text/markdown +author=Jason Thomale +author_email=jason.thomale@unt.edu +maintainer=University of North Texas Libraries +keywords=python, callnumber, callnumbers, call number, call numbers +license=BSD +classifiers= + Development Status :: 5 - Production/Stable + Intended Audience :: Education + Intended Audience :: Developers + Natural Language :: English + License :: OSI Approved :: BSD License + Programming Language :: Python + Programming Language :: Python :: 2 + Programming Language :: Python :: 2.7 + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.5 + 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.11 + Topic :: Software Development +project_urls= + homepage=https://github.com/unt-libraries/pycallnumber + +[options] +package_dir= + =src +packages=find: +install_requires = + future; python_version=="2.7" + importlib_metadata>=2.0.0; python_version<="3.7" + +[options.packages.find] +where=src + +[options.extras_require] +dev = + pytest>=6.2.4; python_version>="3.10" + pytest>=3.5.0; python_version<"3.10" diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..6068493 --- /dev/null +++ b/setup.py @@ -0,0 +1,3 @@ +from setuptools import setup + +setup() diff --git a/src/pycallnumber/__init__.py b/src/pycallnumber/__init__.py index 6876d9f..b9e833a 100644 --- a/src/pycallnumber/__init__.py +++ b/src/pycallnumber/__init__.py @@ -9,7 +9,7 @@ try: from importlib import metadata -except (ImportError, ModuleNotFoundError): +except ImportError: import importlib_metadata as metadata from pycallnumber import settings @@ -29,11 +29,11 @@ _md = metadata.metadata('pycallnumber') __version__ = metadata.version('pycallnumber') __name__ = 'pycallnumber' -__url__ = _md['Project-url'].split(', ')[1] +__url__ = _md['Home-page'] __description__ = _md['Summary'] -__license__ = 'BSD' -__author__ = _md['Author-email'].split(' <')[0] -__author_email__ = _md['Author-email'].split(' <')[1].rstrip('>') +__license__ = _md['License'] +__author__ = _md['Author'] +__author_email__ = _md['Author-email'] __maintainer__ = _md['Maintainer'] __keywords__ = _md['Keywords'] __all__ = ['settings', 'CallNumberError', 'CallNumberWarning', From 1c5b8442da43ae15d90375bdb8c4b167da37ef2e Mon Sep 17 00:00:00 2001 From: Jason Thomale Date: Thu, 17 Nov 2022 12:32:02 -0600 Subject: [PATCH 04/12] Separate building from testing the build Since (for now) we're supporting a very wide range of Python versions, I thought it might be a good idea to be able to test the built package against any/all supported versions. This change removes the final test step from the `build_package` tox environment and adds a new set of tox environments for testing the built package. Note that you must first run `build_package`. That will put a wheel and sdist into a `dist/` directory. Then you can run any number of `test_built_package` jobs and it will test against the built wheel, as long as you don't remove that file. --- pyproject.toml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 53289e7..9e00261 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,9 +55,8 @@ commands = [testenv:build_package] basepython = python3.10 -skip_install = True +skip_install = true deps = - pytest build twine allowlist_externals = @@ -67,6 +66,14 @@ commands = python -m build bash -c 'python -m twine check dist/*.whl' bash -c 'python -m twine check dist/*.gz' - bash -c 'python -m pip install dist/*.whl' + +[testenv:py{27,35,36,37,38,39,310,311}-test_built_package] +skip_install = true +deps = + pytest +allowlist_externals = + bash +commands = + bash -c 'python -m pip install {posargs:dist/*.whl}' pytest """ From 13f0e9c3dcafa8955afe48375c3293cf64c4ebce Mon Sep 17 00:00:00 2001 From: Jason Thomale Date: Thu, 17 Nov 2022 12:46:28 -0600 Subject: [PATCH 05/12] Ensure the build works on Python 2.7 With the recent changes, I neglected to move over the section of the `setup.cfg` file required to build a universal wheel (for Python 2 and 3). This fixes it. --- setup.cfg | 3 +++ 1 file changed, 3 insertions(+) diff --git a/setup.cfg b/setup.cfg index cd6de30..b88a20f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -45,3 +45,6 @@ where=src dev = pytest>=6.2.4; python_version>="3.10" pytest>=3.5.0; python_version<"3.10" + +[bdist_wheel] +universal = 1 From 9c52df0ef499079efc6cfccd1eadab306bb8d1eb Mon Sep 17 00:00:00 2001 From: Jason Thomale Date: Thu, 17 Nov 2022 13:20:49 -0600 Subject: [PATCH 06/12] Add missing pytest markers to ini to fix warnings --- pytest.ini | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pytest.ini b/pytest.ini index b7bae48..e979267 100644 --- a/pytest.ini +++ b/pytest.ini @@ -5,6 +5,9 @@ markers = for_sort: For running only ``for_sort`` tests on units. valid: For running only ``valid`` tests on units. invalid: For running only ``invalid`` tests on units. + valid_parse: For running only ``valid_parse`` tests on units. + parts: For running only ``parts`` tests on units. + attribute: For running only ``attribute`` tests on units. sort: For running only ``sort`` tests on units. sort_equivalence: For running only ``sort_equivalence`` tests on units. search: For running only ``search`` tests on units. @@ -40,4 +43,4 @@ markers = multiple: For running Range and Set tests on multi-arg operators. callnumber_factory: For testing the ``callnumber`` factory. cnrange_factory: For testing the ``cnrange`` factory. - cnset_factory: For testing the ``cnset`` factory. \ No newline at end of file + cnset_factory: For testing the ``cnset`` factory. From 39e2b0040dd43a487aa59e439be639f2aac74bcc Mon Sep 17 00:00:00 2001 From: Jason Thomale Date: Thu, 17 Nov 2022 14:06:06 -0600 Subject: [PATCH 07/12] Remove whitespace for flake8 --- src/pycallnumber/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pycallnumber/__init__.py b/src/pycallnumber/__init__.py index b9e833a..5879962 100644 --- a/src/pycallnumber/__init__.py +++ b/src/pycallnumber/__init__.py @@ -35,7 +35,7 @@ __author__ = _md['Author'] __author_email__ = _md['Author-email'] __maintainer__ = _md['Maintainer'] -__keywords__ = _md['Keywords'] +__keywords__ = _md['Keywords'] __all__ = ['settings', 'CallNumberError', 'CallNumberWarning', 'InvalidCallNumberStringError', 'SettingsError', 'MethodError', 'OptionsError', 'UtilsError', 'RangeSetError', 'BadRange', From de597d75a79f9066732d9cd3f9978cae54776170 Mon Sep 17 00:00:00 2001 From: Jason Thomale Date: Thu, 17 Nov 2022 14:51:19 -0600 Subject: [PATCH 08/12] Add github-actions CI/CD workflow(s) This is heavily based on the workflows I created for fauxdoc. I have two workflows: - do-checks-and-tests.yml - After each push to the repository, it runs tox tests against all specified Python versions. (Including linters.) - If tests pass and the pushed ref is a tag that begins with 'v', it triggers the publish workflow. - publish.yml - When triggered, it uses tox to build, test, and publish the package. - Build: Builds a wheel and sdist and runs twine checks. Uploads the wheel and sdist (in the resulting `dist` directory) as a build artifact. - Test: If the build is successful, this downloads the built package, installs it, and runs tests. - Publish: If tests pass, and this is a pre-release version (based on the version string), then it uploads the package to Test PyPI. If this is a full release, then it also uploads it to live PyPI. --- .github/workflows/do-checks-and-tests.yml | 45 +++++++++++ .github/workflows/publish.yml | 98 +++++++++++++++++++++++ 2 files changed, 143 insertions(+) create mode 100644 .github/workflows/do-checks-and-tests.yml create mode 100644 .github/workflows/publish.yml diff --git a/.github/workflows/do-checks-and-tests.yml b/.github/workflows/do-checks-and-tests.yml new file mode 100644 index 0000000..b032cfd --- /dev/null +++ b/.github/workflows/do-checks-and-tests.yml @@ -0,0 +1,45 @@ +name: Run linters and tests +on: [push, workflow_call, workflow_dispatch] +jobs: + + run-linters: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.10" + - name: Upgrade pip and install tox + run: | + python -m pip install --upgrade pip + python -m pip install tox + - name: Run linter + run: tox -e flake8 + + run-tests: + runs-on: ubuntu-latest + strategy: + matrix: + python: ['2.7', '3.5', '3.6', '3.7', '3.8', '3.9', '3.10', '3.11'] + tox-env: ['27', '35', '36', '37', '38', '39', '310', '311'] + steps: + - uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python }} + - name: Upgrade pip and install tox + run: | + python -m pip install --upgrade pip + python -m pip install tox + - name: Run tests + run: tox -e "py${{ matrix.tox-env }}-{oldest,latest}" + + trigger-publish: + if: ${{ github.ref_type == 'tag' && startsWith(github.ref_name, 'v') }} + needs: [run-linters, run-tests] + uses: ./.github/workflows/publish.yml + secrets: + TEST_PYPI_API_TOKEN: ${{ secrets.TEST_PYPI_API_TOKEN }} + PYPI_API_TOKEN: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..1f9679b --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,98 @@ +name: Build and publish package +on: + workflow_call: + inputs: + skipTestUpload: + description: 'Skip uploading the package to Test PyPI?' + required: false + default: false + type: boolean + skipLiveUpload: + description: 'Skip uploading the package to Live PyPI?' + required: false + default: false + type: boolean + secrets: + TEST_PYPI_API_TOKEN: + required: true + PYPI_API_TOKEN: + required: true + workflow_dispatch: + inputs: + skipTestUpload: + description: 'Skip uploading the package to Test PyPI?' + required: false + default: false + type: boolean + skipLiveUpload: + description: 'Skip uploading the package to Live PyPI?' + required: false + default: false + type: boolean + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.10" + - name: Upgrade pip and install tox + run: | + python -m pip install --upgrade pip + python -m pip install tox + - name: Build the package + run: tox -e build_package + - name: Upload built package + uses: actions/upload-artifact@v3 + with: + name: pycallnumber-dist + path: dist + retention-days: 1 + + test-built-package: + needs: build + runs-on: ubuntu-latest + strategy: + matrix: + python: ['2.7', '3.5', '3.6', '3.7', '3.8', '3.9', '3.10', '3.11'] + tox-env: ['27', '35', '36', '37', '38', '39', '310', '311'] + steps: + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python }} + - name: Upgrade pip and install tox + run: | + python -m pip install --upgrade pip + python -m pip install tox + - name: Download built package + uses: actions/download-artifact@v3 + with: + name: pycallnumber-dist + - name: Run tests + run: tox -e "py${{ matrix.tox-env }}-test_built_package" + + publish: + needs: test-built-package + runs-on: ubuntu-latest + steps: + - name: Download built package + uses: actions/download-artifact@v3 + with: + name: pycallnumber-dist + - name: Publish package to Test PyPI + if: ${{ !inputs.skipTestUpload }} + uses: pypa/gh-action-pypi-publish@release/v1 + with: + password: ${{ secrets.TEST_PYPI_API_TOKEN }} + repository_url: https://test.pypi.org/legacy/ + - name: Publish package to Live PyPI + if: ${{ !inputs.skipLiveUpload && github.ref_type == 'tag' && startsWith(github.ref_name, 'v') && !(contains(github.ref_name, 'dev') || contains(github.ref_name, 'a') || contains(github.ref_name, 'b') || contains(github.ref_name, 'rc')) }} + uses: pypa/gh-action-pypi-publish@release/v1 + with: + password: ${{ secrets.PYPI_API_TOKEN }} From 4386abc12d0a90b7c63f7e40fee528cac952a84a Mon Sep 17 00:00:00 2001 From: Jason Thomale Date: Thu, 17 Nov 2022 15:52:03 -0600 Subject: [PATCH 09/12] Fix python version / tox-env matrix --- .github/workflows/do-checks-and-tests.yml | 19 +++++++++++++++++-- .github/workflows/publish.yml | 19 +++++++++++++++++-- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/.github/workflows/do-checks-and-tests.yml b/.github/workflows/do-checks-and-tests.yml index b032cfd..6ae0496 100644 --- a/.github/workflows/do-checks-and-tests.yml +++ b/.github/workflows/do-checks-and-tests.yml @@ -21,8 +21,23 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python: ['2.7', '3.5', '3.6', '3.7', '3.8', '3.9', '3.10', '3.11'] - tox-env: ['27', '35', '36', '37', '38', '39', '310', '311'] + include: + - python: '2.7' + tox-env: '27' + - python: '3.5' + tox-env: '35' + - python: '3.6' + tox-env: '36' + - python: '3.7' + tox-env: '37' + - python: '3.8' + tox-env: '38' + - python: '3.9' + tox-env: '39' + - python: '3.10' + tox-env: '310' + - python: '3.11' + tox-env: '311' steps: - uses: actions/checkout@v3 - name: Set up Python diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 1f9679b..bb71fb6 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -59,8 +59,23 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python: ['2.7', '3.5', '3.6', '3.7', '3.8', '3.9', '3.10', '3.11'] - tox-env: ['27', '35', '36', '37', '38', '39', '310', '311'] + include: + - python: '2.7' + tox-env: '27' + - python: '3.5' + tox-env: '35' + - python: '3.6' + tox-env: '36' + - python: '3.7' + tox-env: '37' + - python: '3.8' + tox-env: '38' + - python: '3.9' + tox-env: '39' + - python: '3.10' + tox-env: '310' + - python: '3.11' + tox-env: '311' steps: - name: Set up Python uses: actions/setup-python@v4 From d80e342f75c00b4e1211020bb21822ba0b7b2bce Mon Sep 17 00:00:00 2001 From: Jason Thomale Date: Thu, 17 Nov 2022 16:13:39 -0600 Subject: [PATCH 10/12] Fix `test-built-package` step of publish workflow Forgot that it still needs to check out the repository so that it has the tox config and the tests available to use. --- .github/workflows/publish.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index bb71fb6..4341782 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -34,7 +34,8 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - name: Check out repository + uses: actions/checkout@v3 with: fetch-depth: 0 - name: Set up Python @@ -77,6 +78,8 @@ jobs: - python: '3.11' tox-env: '311' steps: + - name: Check out repository + uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 with: @@ -89,7 +92,7 @@ jobs: uses: actions/download-artifact@v3 with: name: pycallnumber-dist - - name: Run tests + - name: Install built package and run tests run: tox -e "py${{ matrix.tox-env }}-test_built_package" publish: From f6f92aabf1f3e33330eb849389f4c4b114423f19 Mon Sep 17 00:00:00 2001 From: Jason Thomale Date: Fri, 18 Nov 2022 08:03:17 -0600 Subject: [PATCH 11/12] Try getting artifact upload/download to work Tox isn't recognizing the wheel file in the dist directory after downloading the artifact. From the examples it looks like the download action should drop the artifact into the working directory, but since the artifact *is* a directory it may not be working the way I think it should. I'm trying to see if tarring/untarring the dist directory will help. --- .github/workflows/publish.yml | 16 +++++++++++++--- .gitignore | 1 + 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 4341782..086d293 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -48,11 +48,13 @@ jobs: python -m pip install tox - name: Build the package run: tox -e build_package - - name: Upload built package + - name: Tar the dist directory + run: tar -cvf dist.tar dist + - name: Upload dist.tar uses: actions/upload-artifact@v3 with: name: pycallnumber-dist - path: dist + path: dist.tar retention-days: 1 test-built-package: @@ -88,10 +90,14 @@ jobs: run: | python -m pip install --upgrade pip python -m pip install tox - - name: Download built package + - name: Download dist.tar uses: actions/download-artifact@v3 with: name: pycallnumber-dist + - name: Un-tar built package + run: | + tar -xvf dist.tar + ls -Rl - name: Install built package and run tests run: tox -e "py${{ matrix.tox-env }}-test_built_package" @@ -103,6 +109,10 @@ jobs: uses: actions/download-artifact@v3 with: name: pycallnumber-dist + - name: Un-tar built package + run: | + tar -xvf dist.tar + ls -Rl - name: Publish package to Test PyPI if: ${{ !inputs.skipTestUpload }} uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.gitignore b/.gitignore index b08889b..8d416da 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ *.pyc dist/ +dist.tar build/ *.egg-info *.egg From d6ce545fda9d46831ed9f4fb74a49164f05c770e Mon Sep 17 00:00:00 2001 From: Jason Thomale Date: Fri, 18 Nov 2022 11:53:09 -0600 Subject: [PATCH 12/12] Remove travis-CI cfg and update README Github actions are now working, so we can remove the old travis-CI config and add the Github actions badge to the README. --- .travis.yml | 27 --------------------------- README.md | 6 ++++-- 2 files changed, 4 insertions(+), 29 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 13e5069..0000000 --- a/.travis.yml +++ /dev/null @@ -1,27 +0,0 @@ -language: python -matrix: - include: - - python: "2.7" - env: TOX_ENV=py27 - - python: "3.4" - env: TOX_ENV=py34 - - python: "3.5" - env: TOX_ENV=py35 - - python: "3.6" - env: TOX_ENV=py36 - - python: "3.7" - dist: xenial - sudo: required - env: TOX_ENV=py37 - - python: "2.7" - env: TOX_ENV=flake8 - allow_failures: - - env: TOX_ENV=flake8 -before_install: - - "pip install --upgrade pip" - - "pip install --upgrade setuptools" - - "pip install tox" -install: - - "pip install ." - -script: tox -e $TOX_ENV \ No newline at end of file diff --git a/README.md b/README.md index 88d072b..84f87e3 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # pycallnumber +[![Build Status](https://github.com/unt-libraries/pycallnumber/actions/workflows/do-checks-and-tests.yml/badge.svg?branch=master)](https://github.com/unt-libraries/pycallnumber/actions) + Use pycallnumber in your library's Python projects to parse, model, and manipulate any type of call number string. Support for Library of Congress, Dewey Decimal, SuDocs, and local call numbers is built in, and you can extend built-in classes to customize behavior or model other types of call numbers and formatted strings. * [Installation](#Installation) @@ -10,11 +12,11 @@ Use pycallnumber in your library's Python projects to parse, model, and manipula ### Requirements -Tests pass on Linux and MacOS Python 2.7, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10, and 3.11. Version 3.4 and below may still work, but I'm unable to get these to compile any more so cannot test them. +Tests pass on Linux and MacOS Python 2.7, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10, and 3.11. Versions 3.4 and below may still work, but I'm unable to get these to compile any more so cannot test them. #### Warning: Outdated Python Versions -The next version of pycallnumber (likely 1.0.0) will drop support for Python versions older than 3.7. +***Warning*** — The next release, v1.0.0, will drop support for Python versions older than 3.7. ### Dependencies