[Git][debian-gis-team/python-affine][master] 7 commits: New upstream version 2.4.0

Bas Couwenberg (@sebastic) gitlab at salsa.debian.org
Fri Jan 20 05:39:02 GMT 2023



Bas Couwenberg pushed to branch master at Debian GIS Project / python-affine


Commits:
2c3486d7 by Bas Couwenberg at 2023-01-20T06:21:14+01:00
New upstream version 2.4.0
- - - - -
af158cd9 by Bas Couwenberg at 2023-01-20T06:21:15+01:00
Update upstream source from tag 'upstream/2.4.0'

Update to upstream version '2.4.0'
with Debian dir 12c13fcdd5c8cc0931347c5f22b515b9437aa731
- - - - -
9ca43630 by Bas Couwenberg at 2023-01-20T06:21:43+01:00
New upstream release.

- - - - -
e2e9082a by Bas Couwenberg at 2023-01-20T06:24:04+01:00
Update GitHub URLs for move to rasterio namespace.

- - - - -
7e652ac6 by Bas Couwenberg at 2023-01-20T06:31:08+01:00
Add pybuild-plugin-pyproject & flit to build dependencies.

- - - - -
36c09535 by Bas Couwenberg at 2023-01-20T06:31:56+01:00
Drop python3-setuptools from build dependencies.

- - - - -
dc11f9cb by Bas Couwenberg at 2023-01-20T06:31:56+01:00
Set distribution to unstable.

- - - - -


21 changed files:

- − .coveragerc
- + .github/codecov.yml
- + .github/workflows/ci.yml
- .gitignore
- − .travis.yml
- CHANGES.txt
- − MANIFEST.in
- README.rst
- affine/__init__.py
- affine/tests/test_rotation.py
- + affine/tests/test_serialize.py
- affine/tests/test_transform.py
- debian/changelog
- debian/control
- debian/copyright
- debian/upstream/metadata
- debian/watch
- + pyproject.toml
- setup.cfg
- − setup.py
- tox.ini


Changes:

=====================================
.coveragerc deleted
=====================================
@@ -1,2 +0,0 @@
-[run]
-omit = affine/tests/*


=====================================
.github/codecov.yml
=====================================
@@ -0,0 +1,8 @@
+comment: off
+
+coverage:
+    status:
+        project:
+            default:
+                target: auto
+                threshold: 5


=====================================
.github/workflows/ci.yml
=====================================
@@ -0,0 +1,82 @@
+name: CI
+
+# On every pull request, but only on push to master
+on:
+  push:
+    branches:
+    - main
+    tags:
+    - '*'
+  pull_request:
+env:
+  LATEST_PY_VERSION: '3.11'
+
+jobs:
+  tests:
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        python-version: ['3.7', '3.8', '3.9', '3.10', '3.11']
+
+    steps:
+      - uses: actions/checkout at v3
+      - name: Set up Python ${{ matrix.python-version }}
+        uses: actions/setup-python at v4
+        with:
+          python-version: ${{ matrix.python-version }}
+
+      - name: Install affine
+        run: |
+          python -m pip install --upgrade pip
+          python -m pip install .["test"]
+
+      - name: Run tests
+        run: |
+          python -m pytest --cov=affine --cov-report=term-missing --cov-report=xml
+
+      - name: Upload Coverage Results
+        if: ${{ matrix.python-version == env.LATEST_PY_VERSION }}
+        uses: codecov/codecov-action at v3
+        with:
+          files: ./coverage.xml
+          flags: unittests
+          fail_ci_if_error: false
+
+  lint:
+    needs: tests
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout at v3
+      - name: Set up Python
+        uses: actions/setup-python at v4
+        with:
+          python-version: ${{ env.LATEST_PY_VERSION }}
+
+      - name: Install dependencies
+        run: |
+          python -m pip install --upgrade pip
+          python -m pip install flake8 pydocstyle
+
+      - name: Run lint
+        run: |
+          flake8 --ignore=E501,W503
+          python -m pydocstyle affine
+
+  typing:
+    needs: tests
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout at v3
+      - name: Set up Python
+        uses: actions/setup-python at v4
+        with:
+          python-version: ${{ env.LATEST_PY_VERSION }}
+
+      - name: Install dependencies
+        run: |
+          python -m pip install --upgrade pip
+          python -m pip install mypy pytest
+
+      - name: Run typing test
+        run: |
+          mypy affine


=====================================
.gitignore
=====================================
@@ -57,3 +57,5 @@ docs/_build/
 
 __pycache__
 *.swp
+
+.coverage.*


=====================================
.travis.yml deleted
=====================================
@@ -1,23 +0,0 @@
-dist: xenial
-language: python
-cache: pip
-python:
-  - 2.7
-  - 3.6
-  - 3.7
-  - pypy3
-install:
-  - pip install flake8 pydocstyle
-  - pip install .[test]
-script:
-  - flake8
-  - python -m pydocstyle affine
-  - python -m pytest --cov affine --cov-report term-missing
-after_success:
-  - coveralls
-deploy:
-  on:
-    tags: true
-  provider: pypi
-  distributions: "sdist bdist_wheel"
-  user: __token__


=====================================
CHANGES.txt
=====================================
@@ -1,6 +1,22 @@
 CHANGES
 =======
 
+2.4.0 (2023-01-19)
+------------------
+
+- Package is marked as Python 3 only, two instances of "%" string formatting
+  are replaced by f-strings (#96).
+
+2.4b1 (2023-01-18)
+------------------
+
+- Elimination of Python 2/3 compatibility code in __gt__ (#94).
+- Addition of optional keyword arguments for __new__, solving an issue with
+  Dask (#92).
+- Addition of some type hints for float arguments and return types (#87)..
+- Python version support is now 3.7-3.11 (#82).
+- Faster __new__ and from_gdal methods (#78).
+
 2.3.1 (2022-03-24)
 ------------------
 


=====================================
MANIFEST.in deleted
=====================================
@@ -1,4 +0,0 @@
-exclude *.rst *.txt *.py
-include CHANGES.txt AUTHORS.txt LICENSE.txt VERSION.txt README.rst setup.py setup.cfg
-recursive-include affine/tests *.py
-exclude MANIFEST.in


=====================================
README.rst
=====================================
@@ -1,13 +1,13 @@
 Affine
 ======
 
-Matrices describing affine transformation of the plane.
+Matrices describing 2D affine transformation of the plane.
 
-.. image:: https://travis-ci.org/sgillies/affine.svg?branch=master
-    :target: https://travis-ci.org/sgillies/affine
+.. image:: https://github.com/rasterio/affine/actions/workflows/ci.yml/badge.svg?branch=main
+    :target: https://github.com/rasterio/affine/actions/workflows/ci.yml
 
-.. image:: https://coveralls.io/repos/sgillies/affine/badge.svg
-    :target: https://coveralls.io/r/sgillies/affine
+.. image:: https://codecov.io/gh/rasterio/affine/branch/main/graph/badge.svg
+    :target: https://codecov.io/gh/rasterio/affine
 
 The Affine package is derived from Casey Duncan's Planar package. Please see
 the copyright statement in `affine/__init__.py <affine/__init__.py>`__.


=====================================
affine/__init__.py
=====================================
@@ -32,18 +32,16 @@ copyright statement below.
 # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #############################################################################
 
-from __future__ import division
-
 from collections import namedtuple
 import math
 import warnings
 
 
-__all__ = ['Affine']
+__all__ = ["Affine"]
 __author__ = "Sean Gillies"
-__version__ = "2.3.1"
+__version__ = "2.4.0"
 
-EPSILON = 1e-5
+EPSILON: float = 1e-5
 
 
 class AffineError(Exception):
@@ -58,26 +56,6 @@ class UndefinedRotationError(AffineError):
     """The rotation angle could not be computed for this transform"""
 
 
-# Define assert_unorderable() depending on the language
-# implicit ordering rules. This keeps things consistent
-# across major Python versions
-try:
-    3 > ""
-except TypeError:  # pragma: no cover
-    # No implicit ordering (newer Python)
-    def assert_unorderable(a, b):
-        """Assert that a and b are unorderable"""
-        return NotImplemented
-else:  # pragma: no cover
-    # Implicit ordering by default (older Python)
-    # We must raise an exception ourselves
-    # To prevent nonsensical ordering
-    def assert_unorderable(a, b):
-        """Assert that a and b are unorderable"""
-        raise TypeError("unorderable types: %s and %s"
-                        % (type(a).__name__, type(b).__name__))
-
-
 def cached_property(func):
     """Special property decorator that caches the computed
     property value in the object's instance dict the first
@@ -92,11 +70,12 @@ def cached_property(func):
         except KeyError:
             self.__dict__[name] = value = func(self)
             return value
+
     getter.func_name = name
     return property(getter, doc=doc)
 
 
-def cos_sin_deg(deg):
+def cos_sin_deg(deg: float):
     """Return the cosine and sin for the given angle in degrees.
 
     With special-case handling of multiples of 90 for perfect right
@@ -113,8 +92,7 @@ def cos_sin_deg(deg):
     return math.cos(rad), math.sin(rad)
 
 
-class Affine(
-        namedtuple('Affine', ('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'))):
+class Affine(namedtuple("Affine", ("a", "b", "c", "d", "e", "f", "g", "h", "i"))):
     """Two dimensional affine transform for 2D linear mapping.
 
     Parameters
@@ -132,7 +110,7 @@ class Affine(
     Attributes
     ----------
     a, b, c, d, e, f, g, h, i : float
-        The coefficients of the 3x3 augumented affine transformation
+        The coefficients of the 3x3 augmented affine transformation
         matrix
 
         | x' |   | a  b  c | | x |
@@ -161,9 +139,21 @@ class Affine(
     users of this class.
 
     """
+
     precision = EPSILON
 
-    def __new__(cls, a, b, c, d, e, f):
+    def __new__(
+        cls,
+        a: float,
+        b: float,
+        c: float,
+        d: float,
+        e: float,
+        f: float,
+        g: float = 0.0,
+        h: float = 0.0,
+        i: float = 1.0,
+    ):
         """Create a new object
 
         Parameters
@@ -171,19 +161,29 @@ class Affine(
         a, b, c, d, e, f : float
             Elements of an augmented affine transformation matrix.
         """
-        mat3x3 = [x * 1.0 for x in [a, b, c, d, e, f]] + [0.0, 0.0, 1.0]
-        return tuple.__new__(cls, mat3x3)
+        return tuple.__new__(
+            cls,
+            (
+                a * 1.0,
+                b * 1.0,
+                c * 1.0,
+                d * 1.0,
+                e * 1.0,
+                f * 1.0,
+                g * 1.0,
+                h * 1.0,
+                i * 1.0,
+            ),
+        )
 
     @classmethod
-    def from_gdal(cls, c, a, b, f, d, e):
+    def from_gdal(cls, c: float, a: float, b: float, f: float, d: float, e: float):
         """Use same coefficient order as GDAL's GetGeoTransform().
 
         :param c, a, b, f, d, e: 6 floats ordered by GDAL.
         :rtype: Affine
         """
-        members = [a, b, c, d, e, f]
-        mat3x3 = [x * 1.0 for x in members] + [0.0, 0.0, 1.0]
-        return tuple.__new__(cls, mat3x3)
+        return cls.__new__(cls, a, b, c, d, e, f)
 
     @classmethod
     def identity(cls):
@@ -194,7 +194,7 @@ class Affine(
         return identity
 
     @classmethod
-    def translation(cls, xoff, yoff):
+    def translation(cls, xoff: float, yoff: float):
         """Create a translation transform from an offset vector.
 
         :param xoff: Translation x offset.
@@ -203,11 +203,7 @@ class Affine(
         :type yoff: float
         :rtype: Affine
         """
-        return tuple.__new__(
-            cls,
-            (1.0, 0.0, xoff,
-             0.0, 1.0, yoff,
-             0.0, 0.0, 1.0))
+        return tuple.__new__(cls, (1.0, 0.0, xoff, 0.0, 1.0, yoff, 0.0, 0.0, 1.0))
 
     @classmethod
     def scale(cls, *scaling):
@@ -223,14 +219,10 @@ class Affine(
             sx = sy = float(scaling[0])
         else:
             sx, sy = scaling
-        return tuple.__new__(
-            cls,
-            (sx, 0.0, 0.0,
-             0.0, sy, 0.0,
-             0.0, 0.0, 1.0))
+        return tuple.__new__(cls, (sx, 0.0, 0.0, 0.0, sy, 0.0, 0.0, 0.0, 1.0))
 
     @classmethod
-    def shear(cls, x_angle=0, y_angle=0):
+    def shear(cls, x_angle: float = 0, y_angle: float = 0):
         """Create a shear transform along one or both axes.
 
         :param x_angle: Shear angle in degrees parallel to the x-axis.
@@ -241,14 +233,10 @@ class Affine(
         """
         mx = math.tan(math.radians(x_angle))
         my = math.tan(math.radians(y_angle))
-        return tuple.__new__(
-            cls,
-            (1.0, mx, 0.0,
-             my, 1.0, 0.0,
-             0.0, 0.0, 1.0))
+        return tuple.__new__(cls, (1.0, mx, 0.0, my, 1.0, 0.0, 0.0, 0.0, 1.0))
 
     @classmethod
-    def rotation(cls, angle, pivot=None):
+    def rotation(cls, angle: float, pivot=None):
         """Create a rotation transform at the specified angle.
 
         A pivot point other than the coordinate system origin may be
@@ -264,18 +252,23 @@ class Affine(
         """
         ca, sa = cos_sin_deg(angle)
         if pivot is None:
-            return tuple.__new__(
-                cls,
-                (ca, -sa, 0.0,
-                 sa, ca, 0.0,
-                 0.0, 0.0, 1.0))
+            return tuple.__new__(cls, (ca, -sa, 0.0, sa, ca, 0.0, 0.0, 0.0, 1.0))
         else:
             px, py = pivot
             return tuple.__new__(
                 cls,
-                (ca, -sa, px - px * ca + py * sa,
-                 sa, ca, py - px * sa - py * ca,
-                 0.0, 0.0, 1.0))
+                (
+                    ca,
+                    -sa,
+                    px - px * ca + py * sa,
+                    sa,
+                    ca,
+                    py - px * sa - py * ca,
+                    0.0,
+                    0.0,
+                    1.0,
+                ),
+            )
 
     @classmethod
     def permutation(cls, *scaling):
@@ -287,22 +280,17 @@ class Affine(
         :rtype: Affine
         """
 
-        return tuple.__new__(
-            cls,
-            (0.0, 1.0, 0.0,
-             1.0, 0.0, 0.0,
-             0.0, 0.0, 1.0))
+        return tuple.__new__(cls, (0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0))
 
-    def __str__(self):
+    def __str__(self) -> str:
         """Concise string representation."""
-        return ("|% .2f,% .2f,% .2f|\n"
-                "|% .2f,% .2f,% .2f|\n"
-                "|% .2f,% .2f,% .2f|") % self
+        return (
+            "|% .2f,% .2f,% .2f|\n" "|% .2f,% .2f,% .2f|\n" "|% .2f,% .2f,% .2f|"
+        ) % self
 
-    def __repr__(self):
+    def __repr__(self) -> str:
         """Precise string representation."""
-        return ("Affine(%r, %r, %r,\n"
-                "       %r, %r, %r)") % self[:6]
+        return ("Affine(%r, %r, %r,\n" "       %r, %r, %r)") % self[:6]
 
     def to_gdal(self):
         """Return same coefficient order as GDAL's SetGeoTransform().
@@ -322,17 +310,17 @@ class Affine(
         return (self.a, self.b, self.d, self.e, self.xoff, self.yoff)
 
     @property
-    def xoff(self):
+    def xoff(self) -> float:
         """Alias for 'c'"""
         return self.c
 
     @property
-    def yoff(self):
+    def yoff(self) -> float:
         """Alias for 'f'"""
         return self.f
 
     @cached_property
-    def determinant(self):
+    def determinant(self) -> float:
         """The determinant of the transform matrix.
 
         This value is equal to the area scaling factor when the
@@ -353,10 +341,10 @@ class Affine(
         # The singular values are the square root of the eigenvalues
         # of the matrix times its transpose, M M*
         # Computing trace and determinant of M M*
-        trace = a**2 + b**2 + d**2 + e**2
-        det = (a * e - b * d)**2
+        trace = a ** 2 + b ** 2 + d ** 2 + e ** 2
+        det = (a * e - b * d) ** 2
 
-        delta = trace**2 / 4 - det
+        delta = trace ** 2 / 4 - det
         if delta < 1e-12:
             delta = 0
 
@@ -365,7 +353,7 @@ class Affine(
         return l1, l2
 
     @property
-    def eccentricity(self):
+    def eccentricity(self) -> float:
         """The eccentricity of the affine transformation.
 
         This value represents the eccentricity of an ellipse under
@@ -377,14 +365,15 @@ class Affine(
         return math.sqrt(l1 ** 2 - l2 ** 2) / l1
 
     @property
-    def rotation_angle(self):
+    def rotation_angle(self) -> float:
         """The rotation angle in degrees of the affine transformation.
 
         This is the rotation angle in degrees of the affine transformation,
         assuming it is in the form M = R S, where R is a rotation and S is a
         scaling.
 
-        Raises UndefinedRotationError for improper and degenerate transformations.
+        Raises UndefinedRotationError for improper and degenerate
+        transformations.
         """
         a, b, _, c, d, _, _, _, _ = self
         if self.is_proper or self.is_degenerate:
@@ -395,25 +384,26 @@ class Affine(
             raise UndefinedRotationError
 
     @property
-    def is_identity(self):
+    def is_identity(self) -> bool:
         """True if this transform equals the identity matrix,
         within rounding limits.
         """
         return self is identity or self.almost_equals(identity, self.precision)
 
     @property
-    def is_rectilinear(self):
+    def is_rectilinear(self) -> bool:
         """True if the transform is rectilinear.
 
         i.e., whether a shape would remain axis-aligned, within rounding
         limits, after applying the transform.
         """
         a, b, c, d, e, f, g, h, i = self
-        return ((abs(a) < self.precision and abs(e) < self.precision) or
-                (abs(d) < self.precision and abs(b) < self.precision))
+        return (abs(a) < self.precision and abs(e) < self.precision) or (
+            abs(d) < self.precision and abs(b) < self.precision
+        )
 
     @property
-    def is_conformal(self):
+    def is_conformal(self) -> bool:
         """True if the transform is conformal.
 
         i.e., if angles between points are preserved after applying the
@@ -424,7 +414,7 @@ class Affine(
         return abs(a * b + d * e) < self.precision
 
     @property
-    def is_orthonormal(self):
+    def is_orthonormal(self) -> bool:
         """True if the transform is orthonormal.
 
         Which means that the transform represents a rigid motion, which
@@ -434,12 +424,14 @@ class Affine(
         always results in a congruent shape.
         """
         a, b, c, d, e, f, g, h, i = self
-        return (self.is_conformal and
-                abs(1.0 - (a * a + d * d)) < self.precision and
-                abs(1.0 - (b * b + e * e)) < self.precision)
+        return (
+            self.is_conformal
+            and abs(1.0 - (a * a + d * d)) < self.precision
+            and abs(1.0 - (b * b + e * e)) < self.precision
+        )
 
     @cached_property
-    def is_degenerate(self):
+    def is_degenerate(self) -> bool:
         """True if this transform is degenerate.
 
         Which means that it will collapse a shape to an effective area
@@ -448,7 +440,7 @@ class Affine(
         return self.determinant == 0.0
 
     @cached_property
-    def is_proper(self):
+    def is_proper(self) -> bool:
         """True if this transform is proper.
 
         Which means that it does not include reflection.
@@ -461,7 +453,7 @@ class Affine(
         a, b, c, d, e, f, _, _, _ = self
         return (a, d), (b, e), (c, f)
 
-    def almost_equals(self, other, precision=EPSILON):
+    def almost_equals(self, other, precision: float = EPSILON) -> bool:
         """Compare transforms for approximate equality.
 
         :param other: Transform being compared.
@@ -474,8 +466,8 @@ class Affine(
                 return False
         return True
 
-    def __gt__(self, other):
-        return assert_unorderable(self, other)
+    def __gt__(self, other) -> bool:
+        return NotImplemented
 
     __ge__ = __lt__ = __le__ = __gt__
 
@@ -505,9 +497,18 @@ class Affine(
             oa, ob, oc, od, oe, of, _, _, _ = other
             return tuple.__new__(
                 self.__class__,
-                (sa * oa + sb * od, sa * ob + sb * oe, sa * oc + sb * of + sc,
-                 sd * oa + se * od, sd * ob + se * oe, sd * oc + se * of + sf,
-                 0.0, 0.0, 1.0))
+                (
+                    sa * oa + sb * od,
+                    sa * ob + sb * oe,
+                    sa * oc + sb * of + sc,
+                    sd * oa + se * od,
+                    sd * ob + se * oe,
+                    sd * oc + se * of + sf,
+                    0.0,
+                    0.0,
+                    1.0,
+                ),
+            )
         else:
             try:
                 vx, vy = other
@@ -528,8 +529,11 @@ class Affine(
         just a guarantee, since we would potentially return the wrong
         answer in that case.
         """
-        warnings.warn("Right multiplication will be prohibited in version 3.0",
-                      DeprecationWarning, stacklevel=2)
+        warnings.warn(
+            "Right multiplication will be prohibited in version 3.0",
+            DeprecationWarning,
+            stacklevel=2,
+        )
         assert not isinstance(other, Affine)
         return self.__mul__(other)
 
@@ -539,7 +543,7 @@ class Affine(
         else:
             return NotImplemented
 
-    def itransform(self, seq):
+    def itransform(self, seq) -> None:
         """Transform a sequence of points or vectors in place.
 
         :param seq: Mutable sequence of :class:`~planar.Vec2` to be
@@ -558,8 +562,7 @@ class Affine(
             is degenerate.
         """
         if self.is_degenerate:
-            raise TransformNotInvertibleError(
-                "Cannot invert degenerate transform")
+            raise TransformNotInvertibleError("Cannot invert degenerate transform")
         idet = 1.0 / self.determinant
         sa, sb, sc, sd, se, sf, _, _, _ = self
         ra = se * idet
@@ -568,9 +571,8 @@ class Affine(
         re = sa * idet
         return tuple.__new__(
             self.__class__,
-            (ra, rb, -sc * ra - sf * rb,
-             rd, re, -sc * rd - sf * re,
-             0.0, 0.0, 1.0))
+            (ra, rb, -sc * ra - sf * rb, rd, re, -sc * rd - sf * re, 0.0, 0.0, 1.0),
+        )
 
     __hash__ = tuple.__hash__  # hash is not inherited in Py 3
 
@@ -592,32 +594,32 @@ identity = Affine(1, 0, 0, 0, 1, 0)
 # Miscellaneous utilities
 
 
-def loadsw(s):
+def loadsw(s: str):
     """Returns Affine from the contents of a world file string.
 
-    This method also translates the coefficients from from center- to
+    This method also translates the coefficients from center- to
     corner-based coordinates.
 
     :param s: str with 6 floats ordered in a world file.
     :rtype: Affine
     """
-    if not hasattr(s, 'split'):
+    if not hasattr(s, "split"):
         raise TypeError("Cannot split input string")
     coeffs = s.split()
     if len(coeffs) != 6:
         raise ValueError("Expected 6 coefficients, found %d" % len(coeffs))
-    a, d, b, e, c, f = [float(x) for x in coeffs]
+    a, d, b, e, c, f = (float(x) for x in coeffs)
     center = tuple.__new__(Affine, [a, b, c, d, e, f, 0.0, 0.0, 1.0])
     return center * Affine.translation(-0.5, -0.5)
 
 
-def dumpsw(obj):
+def dumpsw(obj) -> str:
     """Return string for a world file.
 
-    This method also translates the coefficients from from corner- to
+    This method also translates the coefficients from corner- to
     center-based coordinates.
 
     :rtype: str
     """
     center = obj * Affine.translation(0.5, 0.5)
-    return '\n'.join(repr(getattr(center, x)) for x in list('adbecf')) + '\n'
+    return "\n".join(repr(getattr(center, x)) for x in list("adbecf")) + "\n"


=====================================
affine/tests/test_rotation.py
=====================================
@@ -35,19 +35,21 @@ def test_rotation_matrix():
 
     """
     rot = Affine.rotation(90.0)
-    assert round(rot.a, 15) == round(math.cos(math.pi/2.0), 15)
-    assert round(rot.b, 15) == round(-math.sin(math.pi/2.0), 15)
+    assert round(rot.a, 15) == round(math.cos(math.pi / 2.0), 15)
+    assert round(rot.b, 15) == round(-math.sin(math.pi / 2.0), 15)
     assert rot.c == 0.0
-    assert round(rot.d, 15) == round(math.sin(math.pi/2.0), 15)
-    assert round(rot.e, 15) == round(math.cos(math.pi/2.0), 15)
+    assert round(rot.d, 15) == round(math.sin(math.pi / 2.0), 15)
+    assert round(rot.e, 15) == round(math.cos(math.pi / 2.0), 15)
     assert rot.f == 0.0
 
 
 def test_rotation_matrix_pivot():
     """A rotation matrix with pivot has expected elements"""
     rot = Affine.rotation(90.0, pivot=(1.0, 1.0))
-    exp = (Affine.translation(1.0, 1.0)
-           * Affine.rotation(90.0)
-           * Affine.translation(-1.0, -1.0))
+    exp = (
+        Affine.translation(1.0, 1.0)
+        * Affine.rotation(90.0)
+        * Affine.translation(-1.0, -1.0)
+    )
     for r, e in zip(rot, exp):
         assert round(r, 15) == round(e, 15)


=====================================
affine/tests/test_serialize.py
=====================================
@@ -0,0 +1,8 @@
+"""Test unpacking and repacking affine matrices."""
+
+from affine import Affine
+
+
+def test_issue79():
+    """An affine matrix can be created from an unpacked matrix."""
+    Affine(*Affine.identity())


=====================================
affine/tests/test_transform.py
=====================================
@@ -28,8 +28,6 @@
 
 """Transform unit tests"""
 
-from __future__ import division
-
 import math
 import unittest
 from textwrap import dedent
@@ -41,13 +39,12 @@ from affine import Affine, EPSILON
 
 
 def seq_almost_equal(t1, t2, error=0.00001):
-    assert len(t1) == len(t2), "%r != %r" % (t1, t2)
+    assert len(t1) == len(t2), f"{t1!r} != {t2!r}"
     for m1, m2 in zip(t1, t2):
-        assert abs(m1 - m2) <= error, "%r != %r" % (t1, t2)
+        assert abs(m1 - m2) <= error, f"{t1!r} != {t2!r}"
 
 
 class PyAffineTestCase(unittest.TestCase):
-
     def test_zero_args(self):
         with pytest.raises(TypeError):
             Affine()
@@ -97,168 +94,101 @@ class PyAffineTestCase(unittest.TestCase):
     def test_getitem_wrong_type(self):
         t = Affine(1, 2, 3, 4, 5, 6)
         with pytest.raises(TypeError):
-            t['foobar']
+            t["foobar"]
 
     def test_str(self):
         t = Affine(1.111, 2.222, 3.333, -4.444, -5.555, 6.666)
-        assert str(t) == dedent("""\
+        assert str(t) == dedent(
+            """\
             | 1.11, 2.22, 3.33|
             |-4.44,-5.55, 6.67|
-            | 0.00, 0.00, 1.00|""")
+            | 0.00, 0.00, 1.00|"""
+        )
 
     def test_repr(self):
         t = Affine(1.111, 2.222, 3.456, 4.444, 5.5, 6.25)
-        assert repr(t) == dedent("""\
+        assert repr(t) == dedent(
+            """\
             Affine(1.111, 2.222, 3.456,
-                   4.444, 5.5, 6.25)""")
+                   4.444, 5.5, 6.25)"""
+        )
 
     def test_identity_constructor(self):
         ident = Affine.identity()
         assert isinstance(ident, Affine)
-        assert \
-            tuple(ident) == \
-            (1, 0, 0,
-             0, 1, 0,
-             0, 0, 1)
+        assert tuple(ident) == (1, 0, 0, 0, 1, 0, 0, 0, 1)
         assert ident.is_identity
 
     def test_permutation_constructor(self):
         perm = Affine.permutation()
         assert isinstance(perm, Affine)
-        assert \
-            tuple(perm) == \
-            (0, 1, 0,
-             1, 0, 0,
-             0, 0, 1)
-        assert (perm*perm).is_identity
+        assert tuple(perm) == (0, 1, 0, 1, 0, 0, 0, 0, 1)
+        assert (perm * perm).is_identity
 
     def test_translation_constructor(self):
         trans = Affine.translation(2, -5)
         assert isinstance(trans, Affine)
-        assert \
-            tuple(trans) == \
-            (1, 0, 2,
-             0, 1, -5,
-             0, 0, 1)
+        assert tuple(trans) == (1, 0, 2, 0, 1, -5, 0, 0, 1)
 
     def test_scale_constructor(self):
         scale = Affine.scale(5)
         assert isinstance(scale, Affine)
-        assert \
-            tuple(scale) == \
-            (5, 0, 0,
-             0, 5, 0,
-             0, 0, 1)
+        assert tuple(scale) == (5, 0, 0, 0, 5, 0, 0, 0, 1)
         scale = Affine.scale(-1, 2)
-        assert \
-            tuple(scale) == \
-            (-1, 0, 0,
-             0, 2, 0,
-             0, 0, 1)
+        assert tuple(scale) == (-1, 0, 0, 0, 2, 0, 0, 0, 1)
         assert tuple(Affine.scale(1)) == tuple(Affine.identity())
 
     def test_shear_constructor(self):
         shear = Affine.shear(30)
         assert isinstance(shear, Affine)
         mx = math.tan(math.radians(30))
-        seq_almost_equal(
-            tuple(shear),
-            (1, mx, 0,
-             0, 1, 0,
-             0, 0, 1))
+        seq_almost_equal(tuple(shear), (1, mx, 0, 0, 1, 0, 0, 0, 1))
         shear = Affine.shear(-15, 60)
         mx = math.tan(math.radians(-15))
         my = math.tan(math.radians(60))
-        seq_almost_equal(
-            tuple(shear),
-            (1, mx, 0,
-             my, 1, 0,
-             0, 0, 1))
+        seq_almost_equal(tuple(shear), (1, mx, 0, my, 1, 0, 0, 0, 1))
         shear = Affine.shear(y_angle=45)
-        seq_almost_equal(
-            tuple(shear),
-            (1, 0, 0,
-             1, 1, 0,
-             0, 0, 1))
+        seq_almost_equal(tuple(shear), (1, 0, 0, 1, 1, 0, 0, 0, 1))
 
     def test_rotation_constructor(self):
         rot = Affine.rotation(60)
         assert isinstance(rot, Affine)
         r = math.radians(60)
         s, c = math.sin(r), math.cos(r)
-        assert \
-            tuple(rot) == \
-            (c, -s, 0,
-             s, c, 0,
-             0, 0, 1)
+        assert tuple(rot) == (c, -s, 0, s, c, 0, 0, 0, 1)
         rot = Affine.rotation(337)
         r = math.radians(337)
         s, c = math.sin(r), math.cos(r)
-        seq_almost_equal(
-            tuple(rot),
-            (c, -s, 0,
-             s, c, 0,
-             0, 0, 1))
+        seq_almost_equal(tuple(rot), (c, -s, 0, s, c, 0, 0, 0, 1))
         assert tuple(Affine.rotation(0)) == tuple(Affine.identity())
 
     def test_rotation_constructor_quadrants(self):
-        assert \
-            tuple(Affine.rotation(0)) == \
-            (1, 0, 0,
-             0, 1, 0,
-             0, 0, 1)
-        assert \
-            tuple(Affine.rotation(90)) == \
-            (0, -1, 0,
-             1, 0, 0,
-             0, 0, 1)
-        assert \
-            tuple(Affine.rotation(180)) == \
-            (-1, 0, 0,
-             0, -1, 0,
-             0, 0, 1)
-        assert \
-            tuple(Affine.rotation(-180)) == \
-            (-1, 0, 0,
-             0, -1, 0,
-             0, 0, 1)
-        assert \
-            tuple(Affine.rotation(270)) == \
-            (0, 1, 0,
-             -1, 0, 0,
-             0, 0, 1)
-        assert \
-            tuple(Affine.rotation(-90)) == \
-            (0, 1, 0,
-             -1, 0, 0,
-             0, 0, 1)
-        assert \
-            tuple(Affine.rotation(360)) == \
-            (1, 0, 0,
-             0, 1, 0,
-             0, 0, 1)
-        assert \
-            tuple(Affine.rotation(450)) == \
-            (0, -1, 0,
-             1, 0, 0,
-             0, 0, 1)
-        assert \
-            tuple(Affine.rotation(-450)) == \
-            (0, 1, 0,
-             -1, 0, 0,
-             0, 0, 1)
+        assert tuple(Affine.rotation(0)) == (1, 0, 0, 0, 1, 0, 0, 0, 1)
+        assert tuple(Affine.rotation(90)) == (0, -1, 0, 1, 0, 0, 0, 0, 1)
+        assert tuple(Affine.rotation(180)) == (-1, 0, 0, 0, -1, 0, 0, 0, 1)
+        assert tuple(Affine.rotation(-180)) == (-1, 0, 0, 0, -1, 0, 0, 0, 1)
+        assert tuple(Affine.rotation(270)) == (0, 1, 0, -1, 0, 0, 0, 0, 1)
+        assert tuple(Affine.rotation(-90)) == (0, 1, 0, -1, 0, 0, 0, 0, 1)
+        assert tuple(Affine.rotation(360)) == (1, 0, 0, 0, 1, 0, 0, 0, 1)
+        assert tuple(Affine.rotation(450)) == (0, -1, 0, 1, 0, 0, 0, 0, 1)
+        assert tuple(Affine.rotation(-450)) == (0, 1, 0, -1, 0, 0, 0, 0, 1)
 
     def test_rotation_constructor_with_pivot(self):
-        assert tuple(Affine.rotation(60)) == \
-            tuple(Affine.rotation(60, pivot=(0, 0)))
+        assert tuple(Affine.rotation(60)) == tuple(Affine.rotation(60, pivot=(0, 0)))
         rot = Affine.rotation(27, pivot=(2, -4))
         r = math.radians(27)
         s, c = math.sin(r), math.cos(r)
-        assert \
-            tuple(rot) == \
-            (c, -s, 2 - 2 * c - 4 * s,
-             s, c, -4 - 2 * s + 4 * c,
-             0, 0, 1)
+        assert tuple(rot) == (
+            c,
+            -s,
+            2 - 2 * c - 4 * s,
+            s,
+            c,
+            -4 - 2 * s + 4 * c,
+            0,
+            0,
+            1,
+        )
         assert tuple(Affine.rotation(0, (-3, 2))) == tuple(Affine.identity())
 
     def test_rotation_contructor_wrong_arg_types(self):
@@ -297,7 +227,7 @@ class PyAffineTestCase(unittest.TestCase):
         assert Affine.rotation(90).is_orthonormal
         assert Affine.rotation(-26).is_orthonormal
         assert not Affine.scale(2.5, 6.1).is_orthonormal
-        assert not Affine.scale(.5, 2).is_orthonormal
+        assert not Affine.scale(0.5, 2).is_orthonormal
         assert not Affine.shear(4, -1).is_orthonormal
 
     def test_is_degenerate(self):
@@ -393,7 +323,7 @@ class PyAffineTestCase(unittest.TestCase):
 
         A = Affine.rotation(33)
         pts = [(4, 1), (-1, 0), (3, 2)]
-        pts_expect = [A*pt for pt in pts]
+        pts_expect = [A * pt for pt in pts]
         r = A.itransform(pts)
         assert r is None
         assert pts == pts_expect
@@ -422,10 +352,8 @@ class PyAffineTestCase(unittest.TestCase):
 
     def test_inverse(self):
         seq_almost_equal(~Affine.identity(), Affine.identity())
-        seq_almost_equal(
-            ~Affine.translation(2, -3), Affine.translation(-2, 3))
-        seq_almost_equal(
-            ~Affine.rotation(-33.3), Affine.rotation(33.3))
+        seq_almost_equal(~Affine.translation(2, -3), Affine.translation(-2, 3))
+        seq_almost_equal(~Affine.rotation(-33.3), Affine.rotation(33.3))
         t = Affine(1, 2, 3, 4, 5, 6)
         seq_almost_equal(~t * t, Affine.identity())
 
@@ -437,36 +365,40 @@ class PyAffineTestCase(unittest.TestCase):
     def test_bad_type_world(self):
         """wrong type, i.e don't use readlines()"""
         with pytest.raises(TypeError):
-            affine.loadsw(['1.0', '0.0', '0.0', '1.0', '0.0', '0.0'])
+            affine.loadsw(["1.0", "0.0", "0.0", "1.0", "0.0", "0.0"])
 
     def test_bad_value_world(self):
         """Wrong number of parameters."""
         with pytest.raises(ValueError):
-            affine.loadsw('1.0\n0.0\n0.0\n1.0\n0.0\n0.0\n0.0')
+            affine.loadsw("1.0\n0.0\n0.0\n1.0\n0.0\n0.0\n0.0")
 
     def test_simple_world(self):
-        s = '1.0\n0.0\n0.0\n-1.0\n100.5\n199.5\n'
+        s = "1.0\n0.0\n0.0\n-1.0\n100.5\n199.5\n"
         a = affine.loadsw(s)
-        assert \
-            a == \
-            Affine(
-                1.0, 0.0, 100.0,
-                0.0, -1., 200.0)
+        assert a == Affine(1.0, 0.0, 100.0, 0.0, -1.0, 200.0)
         assert affine.dumpsw(a) == s
 
     def test_real_world(self):
-        s = dedent('''\
+        s = dedent(
+            """\
                  39.9317755024
                  30.0907511581
                  30.0907511576
                 -39.9317755019
             2658137.2266720217
-            5990821.7039887439''')  # no EOL
+            5990821.7039887439"""
+        )  # no EOL
         a1 = affine.loadsw(s)
         assert a1.almost_equals(
             Affine(
-                39.931775502364644, 30.090751157602412, 2658102.2154086917,
-                30.090751157602412, -39.931775502364644, 5990826.624500916))
+                39.931775502364644,
+                30.090751157602412,
+                2658102.2154086917,
+                30.090751157602412,
+                -39.931775502364644,
+                5990826.624500916,
+            )
+        )
         a1out = affine.dumpsw(a1)
         assert isinstance(a1out, str)
         a2 = affine.loadsw(a1out)
@@ -476,6 +408,7 @@ class PyAffineTestCase(unittest.TestCase):
 # We're using pytest for tests added after 1.0 and don't need unittest
 # test case classes.
 
+
 def test_gdal():
     t = Affine.from_gdal(-237481.5, 425.0, 0.0, 237536.4, 0.0, -425.0)
     assert t.c == t.xoff == -237481.5
@@ -522,18 +455,18 @@ def test_transform_precision():
 
 def test_associative():
     point = (12, 5)
-    trans = Affine.translation(-10., -5.)
-    rot90 = Affine.rotation(90.)
+    trans = Affine.translation(-10.0, -5.0)
+    rot90 = Affine.rotation(90.0)
     result1 = rot90 * (trans * point)
     result2 = (rot90 * trans) * point
-    seq_almost_equal(result1, (0., 2.))
+    seq_almost_equal(result1, (0.0, 2.0))
     seq_almost_equal(result1, result2)
 
 
 def test_roundtrip():
     point = (12, 5)
     trans = Affine.translation(3, 4)
-    rot37 = Affine.rotation(37.)
+    rot37 = Affine.rotation(37.0)
     point_prime = (trans * rot37) * point
     roundtrip_point = ~(trans * rot37) * point_prime
     seq_almost_equal(point, roundtrip_point)
@@ -552,17 +485,15 @@ def test_eccentricity():
 
 
 def test_eccentricity_complex():
-    assert \
-        (Affine.scale(2, 3) * Affine.rotation(77)).eccentricity == \
-        pytest.approx(math.sqrt(5) / 3)
-    assert \
-        (Affine.rotation(77) * Affine.scale(2, 3)).eccentricity == \
-        pytest.approx(math.sqrt(5) / 3)
-    assert \
-        (Affine.translation(32, -47) *
-         Affine.rotation(77) *
-         Affine.scale(2, 3)).eccentricity == \
-        pytest.approx(math.sqrt(5) / 3)
+    assert (Affine.scale(2, 3) * Affine.rotation(77)).eccentricity == pytest.approx(
+        math.sqrt(5) / 3
+    )
+    assert (Affine.rotation(77) * Affine.scale(2, 3)).eccentricity == pytest.approx(
+        math.sqrt(5) / 3
+    )
+    assert (
+        Affine.translation(32, -47) * Affine.rotation(77) * Affine.scale(2, 3)
+    ).eccentricity == pytest.approx(math.sqrt(5) / 3)
 
 
 def test_rotation_angle():
@@ -585,6 +516,7 @@ def test_mul_fallback_unpack():
 
     class TextPoint:
         """Not iterable, will trigger ValueError in Affine.__mul__."""
+
         def __rmul__(self, other):
             return other * (1, 2)
 
@@ -597,6 +529,7 @@ def test_mul_fallback_type_error():
 
     class TextPoint:
         """Iterable, but values trigger TypeError in Affine.__mul__."""
+
         def __iter__(self):
             return ("1", "2")
 
@@ -606,5 +539,5 @@ def test_mul_fallback_type_error():
     assert Affine.identity() * TextPoint() == (1, 2)
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     unittest.main()


=====================================
debian/changelog
=====================================
@@ -1,10 +1,14 @@
-python-affine (2.3.1-2) UNRELEASED; urgency=medium
+python-affine (2.4.0-1) unstable; urgency=medium
 
   * Team upload.
+  * New upstream release.
   * Bump Standards-Version to 4.6.2, no changes.
   * Add Rules-Requires-Root to control file.
+  * Update GitHub URLs for move to rasterio namespace.
+  * Add pybuild-plugin-pyproject & flit to build dependencies.
+  * Drop python3-setuptools from build dependencies.
 
- -- Bas Couwenberg <sebastic at debian.org>  Tue, 21 Jun 2022 07:19:30 +0200
+ -- Bas Couwenberg <sebastic at debian.org>  Fri, 20 Jan 2023 06:25:15 +0100
 
 python-affine (2.3.1-1) unstable; urgency=medium
 


=====================================
debian/control
=====================================
@@ -5,13 +5,14 @@ Section: python
 Priority: optional
 Build-Depends: debhelper-compat (= 12),
                dh-python,
+               flit,
+               pybuild-plugin-pyproject,
                python3-all,
-               python3-pytest,
-               python3-setuptools
+               python3-pytest
 Standards-Version: 4.6.2
 Vcs-Browser: https://salsa.debian.org/debian-gis-team/python-affine
 Vcs-Git: https://salsa.debian.org/debian-gis-team/python-affine.git
-Homepage: https://github.com/sgillies/affine
+Homepage: https://github.com/rasterio/affine
 Rules-Requires-Root: no
 
 Package: python3-affine


=====================================
debian/copyright
=====================================
@@ -1,7 +1,7 @@
 Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
 Upstream-Contact: Sean Gillies <sean.gillies at gmail.com>
 Upstream-Name: affine
-Source: https://github.com/sgillies/affine
+Source: https://github.com/rasterio/affine
 
 Files: *
 Copyright: 2014, Sean Gillies <sean.gillies at gmail.com>


=====================================
debian/upstream/metadata
=====================================
@@ -1,5 +1,5 @@
 ---
-Bug-Database: https://github.com/sgillies/affine/issues
-Bug-Submit: https://github.com/sgillies/affine/issues/new
-Repository: https://github.com/sgillies/affine.git
-Repository-Browse: https://github.com/sgillies/affine
+Bug-Database: https://github.com/rasterio/affine/issues
+Bug-Submit: https://github.com/rasterio/affine/issues/new
+Repository: https://github.com/rasterio/affine.git
+Repository-Browse: https://github.com/rasterio/affine


=====================================
debian/watch
=====================================
@@ -3,5 +3,5 @@ opts=\
 dversionmangle=s/\+(debian|dfsg|ds|deb)\d*$//,\
 uversionmangle=s/_/./g;s/(\d)[_\.\-\+]?((RC|rc|pre|dev|beta|alpha|b|a)[\-\.]?\d*)$/$1~$2/,\
 filenamemangle=s/(?:.*?)?(?:rel|v|affine)?[\-\_]?(\d[\d\.]*)\.(tgz|tbz|txz|(?:tar\.(?:gz|bz2|xz)))/affine-$1.$2/ \
-https://github.com/sgillies/affine/tags \
+https://github.com/rasterio/affine/tags \
 (?:.*?/archive/(?:.*?/)?)?(?:rel|v|affine|)[\-\_]?(\d[\d\-\.]+)\.(?:tgz|tbz|txz|(?:tar\.(?:gz|bz2|xz)))


=====================================
pyproject.toml
=====================================
@@ -0,0 +1,44 @@
+[build-system]
+requires = ["flit_core >=3.2,<4"]
+build-backend = "flit_core.buildapi"
+
+[project]
+name = "affine"
+description = "Matrices describing affine transformation of the plane"
+dynamic = ["version"]
+authors = [
+    {name = "Sean Gillies", email = "sean.gillies at gmail.com"},
+]
+readme = "README.rst"
+keywords = ["affine", "transformation", "matrix"]
+classifiers = [
+    "Development Status :: 5 - Production/Stable",
+    "License :: OSI Approved :: BSD License",
+    "Operating System :: OS Independent",
+    "Programming Language :: Python :: 3 :: Only",
+    "Topic :: Multimedia :: Graphics :: Graphics Conversion",
+    "Topic :: Scientific/Engineering :: GIS",
+]
+license = {text = "BSD-3-Clause"}
+requires-python = ">=3.7"
+
+[project.optional-dependencies]
+test = [
+    "pytest >=4.6",
+    "pytest-cov",
+]
+dev = [
+    "pydocstyle",
+    "flake8",
+    "coveralls",
+]
+
+[project.urls]
+Source = "https://github.com/rasterio/affine"
+
+[tool.flit.sdist]
+include = [
+    "AUTHORS.txt",
+    "CHANGES.txt",
+    "LICENSE.txt",
+]


=====================================
setup.cfg
=====================================
@@ -1,6 +1,3 @@
-[bdist_wheel]
-universal: 1
-
 [tool:pytest]
 testpaths: affine/tests
 


=====================================
setup.py deleted
=====================================
@@ -1,35 +0,0 @@
-import codecs
-import os
-
-from setuptools import find_packages, setup
-
-
-# Parse the version from the affine module.
-with codecs.open(os.path.join("affine", "__init__.py")) as f:
-    for line in f:
-        if "__version__" in line:
-            version = line.split("=")[1].strip()
-            version = version.strip('"').strip("'")
-            break
-
-with codecs.open("README.rst", encoding="utf-8") as f:
-    readme = f.read()
-
-
-setup(
-    name="affine",
-    version=version,
-    description="Matrices describing affine transformation of the plane.",
-    long_description=readme,
-    classifiers=[],
-    keywords="affine transformation matrix",
-    author="Sean Gillies",
-    author_email="sean at mapbox.com",
-    url="https://github.com/sgillies/affine",
-    license="BSD",
-    packages=find_packages(exclude=["ez_setup", "examples", "tests"]),
-    include_package_data=True,
-    zip_safe=True,
-    extras_require={"test": ["pytest>=4.6", "pytest-cov", "pydocstyle",
-                             "flake8", "coveralls"]},
-)


=====================================
tox.ini
=====================================
@@ -1,11 +1,11 @@
 [tox]
-envlist = 
-    py27,py36,py37
+envlist =
+    py37,py38,py39,py310,py311
 
 [testenv]
 usedevelop = true
 deps =
     pytest-cov
     responses
-commands = 
+commands =
     python -m pytest affine/tests --cov affine --cov-report term-missing



View it on GitLab: https://salsa.debian.org/debian-gis-team/python-affine/-/compare/cd33cd8f90d1be7c4db773226b46bda6df7f25e2...dc11f9cb4499887a39b2a651855ae28599fba36f

-- 
View it on GitLab: https://salsa.debian.org/debian-gis-team/python-affine/-/compare/cd33cd8f90d1be7c4db773226b46bda6df7f25e2...dc11f9cb4499887a39b2a651855ae28599fba36f
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20230120/d23d8fe6/attachment-0001.htm>


More information about the Pkg-grass-devel mailing list