diff --git a/configure.ac b/configure.ac index d187707a7a9b771a10454da52707438fd1400a4e..d365275008302daccead20bc88247b59a22a89ca 100644 --- a/configure.ac +++ b/configure.ac @@ -325,7 +325,7 @@ AS_IF([test "x$with_python" != "xno"], [have_python=no]) AS_IF([test "x$have_python" = "xyes"], [ - PYTHON_INC=`$PYTHON -c 'import sys; from distutils import sysconfig; sys.stdout.write(sysconfig.get_python_inc())'` + PYTHON_INC=`$PYTHON -c 'import sys; import sysconfig; sys.stdout.write(sysconfig.get_path("include"))'` AC_CHECK_FILE(["${PYTHON_INC}/Python.h"], [PYMUSIC_CPPFLAGS="-I${PYTHON_INC}"; PYBUFFER_CPPFLAGS="-I${PYTHON_INC}"], [have_python=no]) diff --git a/pymusic/Makefile.am b/pymusic/Makefile.am index beec0f4fa88dfe92598686db29b43c299e691bf9..d62a2a0a7698461cd8f1e2cf429466dd5ef4dd14 100644 --- a/pymusic/Makefile.am +++ b/pymusic/Makefile.am @@ -4,7 +4,8 @@ SUBDIRS = examples EXTRA_DIST = setup.py.in tests.py pymusic.pyx pymusic.pxd pybuffer.pyx pybuffer.pxd \ music/__init__.py music/music_c.h music/pybuffer.pxd music/pymusic.pxd \ - music/pymusic_c.h mpi_compat.h + music/pymusic_c.h mpi_compat.h \ + looseversion/__init__.py looseversion/LICENSE BUILT_SOURCES = pymusic.cpp pybuffer.cpp diff --git a/pymusic/looseversion/LICENSE b/pymusic/looseversion/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..e9d00ca8decc169205dacbbbe62104b2a99d1070 --- /dev/null +++ b/pymusic/looseversion/LICENSE @@ -0,0 +1,52 @@ +The following GPL-compatible license applies to __init__.py in this +directory which was obtained from +https://github.com/effigies/looseversion.git + +PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 +-------------------------------------------- + +1. This LICENSE AGREEMENT is between the Python Software Foundation +("PSF"), and the Individual or Organization ("Licensee") accessing and +otherwise using this software ("Python") in source or binary form and +its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF hereby +grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, +analyze, test, perform and/or display publicly, prepare derivative works, +distribute, and otherwise use Python alone or in any derivative version, +provided, however, that PSF's License Agreement and PSF's notice of copyright, +i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, +2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022 Python Software Foundation; +All Rights Reserved" are retained in Python alone or in any derivative version +prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python. + +4. PSF is making Python available to Licensee on an "AS IS" +basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any +relationship of agency, partnership, or joint venture between PSF and +Licensee. This License Agreement does not grant permission to use PSF +trademarks or trade name in a trademark sense to endorse or promote +products or services of Licensee, or any third party. + +8. By copying, installing or otherwise using Python, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. diff --git a/pymusic/looseversion/__init__.py b/pymusic/looseversion/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..25a5d2a9b232ab573926439a012146b253744e73 --- /dev/null +++ b/pymusic/looseversion/__init__.py @@ -0,0 +1,209 @@ +"""Provides classes to represent module version numbers (one class for +each style of version numbering). There are currently two such classes +implemented: StrictVersion and LooseVersion. + +Every version number class implements the following interface: + * the 'parse' method takes a string and parses it to some internal + representation; if the string is an invalid version number, + 'parse' raises a ValueError exception + * the class constructor takes an optional string argument which, + if supplied, is passed to 'parse' + * __str__ reconstructs the string that was passed to 'parse' (or + an equivalent string -- ie. one that will generate an equivalent + version number instance) + * __repr__ generates Python code to recreate the version number instance + * _cmp compares the current instance with either another instance + of the same class or a string (which will be parsed to an instance + of the same class, thus must follow the same rules) +""" +from __future__ import annotations + +import re +import sys + +# The rules according to Greg Stein: +# 1) a version number has 1 or more numbers separated by a period or by +# sequences of letters. If only periods, then these are compared +# left-to-right to determine an ordering. +# 2) sequences of letters are part of the tuple for comparison and are +# compared lexicographically +# 3) recognize the numeric components may have leading zeroes +# +# The LooseVersion class below implements these rules: a version number +# string is split up into a tuple of integer and string components, and +# comparison is a simple tuple comparison. This means that version +# numbers behave in a predictable and obvious way, but a way that might +# not necessarily be how people *want* version numbers to behave. There +# wouldn't be a problem if people could stick to purely numeric version +# numbers: just split on period and compare the numbers as tuples. +# However, people insist on putting letters into their version numbers; +# the most common purpose seems to be: +# - indicating a "pre-release" version +# ('alpha', 'beta', 'a', 'b', 'pre', 'p') +# - indicating a post-release patch ('p', 'pl', 'patch') +# but of course this can't cover all version number schemes, and there's +# no way to know what a programmer means without asking him. +# +# The problem is what to do with letters (and other non-numeric +# characters) in a version number. The current implementation does the +# obvious and predictable thing: keep them as strings and compare +# lexically within a tuple comparison. This has the desired effect if +# an appended letter sequence implies something "post-release": +# eg. "0.99" < "0.99pl14" < "1.0", and "5.001" < "5.001m" < "5.002". +# +# However, if letters in a version number imply a pre-release version, +# the "obvious" thing isn't correct. Eg. you would expect that +# "1.5.1" < "1.5.2a2" < "1.5.2", but under the tuple/lexical comparison +# implemented here, this just isn't so. +# +# Two possible solutions come to mind. The first is to tie the +# comparison algorithm to a particular set of semantic rules, as has +# been done in the StrictVersion class above. This works great as long +# as everyone can go along with bondage and discipline. Hopefully a +# (large) subset of Python module programmers will agree that the +# particular flavour of bondage and discipline provided by StrictVersion +# provides enough benefit to be worth using, and will submit their +# version numbering scheme to its domination. The free-thinking +# anarchists in the lot will never give in, though, and something needs +# to be done to accommodate them. +# +# Perhaps a "moderately strict" version class could be implemented that +# lets almost anything slide (syntactically), and makes some heuristic +# assumptions about non-digits in version number strings. This could +# sink into special-case-hell, though; if I was as talented and +# idiosyncratic as Larry Wall, I'd go ahead and implement a class that +# somehow knows that "1.2.1" < "1.2.2a2" < "1.2.2" < "1.2.2pl3", and is +# just as happy dealing with things like "2g6" and "1.13++". I don't +# think I'm smart enough to do it right though. +# +# In any case, I've coded the test suite for this module (see +# ../test/test_version.py) specifically to fail on things like comparing +# "1.2a2" and "1.2". That's not because the *code* is doing anything +# wrong, it's because the simple, obvious design doesn't match my +# complicated, hairy expectations for real-world version numbers. It +# would be a snap to fix the test suite to say, "Yep, LooseVersion does +# the Right Thing" (ie. the code matches the conception). But I'd rather +# have a conception that matches common notions about version numbers. + + +class LooseVersion: + + """Version numbering for anarchists and software realists. + Implements the standard interface for version number classes as + described above. A version number consists of a series of numbers, + separated by either periods or strings of letters. When comparing + version numbers, the numeric components will be compared + numerically, and the alphabetic components lexically. The following + are all valid version numbers, in no particular order: + + 1.5.1 + 1.5.2b2 + 161 + 3.10a + 8.02 + 3.4j + 1996.07.12 + 3.2.pl0 + 3.1.1.6 + 2g6 + 11g + 0.960923 + 2.2beta29 + 1.13++ + 5.5.kw + 2.0b1pl0 + + In fact, there is no such thing as an invalid version number under + this scheme; the rules for comparison are simple and predictable, + but may not always give the results you want (for some definition + of "want"). + """ + + component_re: re.Pattern[str] = re.compile(r"(\d+ | [a-z]+ | \.)", re.VERBOSE) + vstring: str + version: list[int | str] + + def __init__(self, vstring: str | None = None): + if vstring: + self.parse(vstring) + + def __eq__(self, other: object) -> bool: + c = self._cmp(other) + if c is NotImplemented: + return NotImplemented + return c == 0 + + def __lt__(self, other: object) -> bool: + c = self._cmp(other) + if c is NotImplemented: + return NotImplemented + return c < 0 + + def __le__(self, other: object) -> bool: + c = self._cmp(other) + if c is NotImplemented: + return NotImplemented + return c <= 0 + + def __gt__(self, other: object) -> bool: + c = self._cmp(other) + if c is NotImplemented: + return NotImplemented + return c > 0 + + def __ge__(self, other: object) -> bool: + c = self._cmp(other) + if c is NotImplemented: + return NotImplemented + return c >= 0 + + def parse(self, vstring: str) -> None: + # I've given up on thinking I can reconstruct the version string + # from the parsed tuple -- so I just store the string here for + # use by __str__ + self.vstring = vstring + components: list[str | int] = [ + x for x in self.component_re.split(vstring) if x and x != "." + ] + for i, obj in enumerate(components): + try: + components[i] = int(obj) + except ValueError: + pass + + self.version = components + + def __str__(self) -> str: + return self.vstring + + def __repr__(self) -> str: + return "LooseVersion ('%s')" % str(self) + + def _cmp(self, other: object) -> int: + other = self._coerce(other) + if other is NotImplemented: + return NotImplemented + + if self.version == other.version: + return 0 + if self.version < other.version: + return -1 + if self.version > other.version: + return 1 + return NotImplemented + + @staticmethod + def _coerce(other: object) -> LooseVersion: + if isinstance(other, LooseVersion): + return other + elif isinstance(other, str): + return LooseVersion(other) + elif "distutils" in sys.modules: + # Using this check to avoid importing distutils and suppressing the warning + try: + from distutils.version import LooseVersion as deprecated + except ImportError: + return NotImplemented + if isinstance(other, deprecated): + return LooseVersion(str(other)) + return NotImplemented diff --git a/pymusic/setup.py.in b/pymusic/setup.py.in index b73ada0c5a903ff4e1ed5c133481c5a3335ac45d..29bbac2401187ebcdc5000279e8be2d91a70d5b3 100644 --- a/pymusic/setup.py.in +++ b/pymusic/setup.py.in @@ -1,4 +1,4 @@ -from distutils.core import setup +from setuptools import setup setup( name='@PACKAGE_NAME@', diff --git a/pymusic/tests.py b/pymusic/tests.py index 50c7407ffdd55a304dbfc703f47166c98073e00f..da5506dad15fdc427658450a1bd3ec041fd7e7de 100644 --- a/pymusic/tests.py +++ b/pymusic/tests.py @@ -2,7 +2,7 @@ # Test mpi4py version import mpi4py -from distutils.version import LooseVersion +from looseversion import LooseVersion is_v2 = LooseVersion(mpi4py.__version__) > LooseVersion("1.3.1") # Write configuration file