[med-svn] [Git][med-team/patsy][upstream] New upstream version 1.0.2

Nilesh Patra (@nilesh) gitlab at salsa.debian.org
Sat Oct 25 11:13:01 BST 2025



Nilesh Patra pushed to branch upstream at Debian Med / patsy


Commits:
e40f1ec5 by Nilesh Patra at 2025-10-25T15:33:56+05:30
New upstream version 1.0.2
- - - - -


21 changed files:

- − .github/workflows/publish.yml
- − .github/workflows/tox.yml
- .pre-commit-config.yaml
- README.md
- doc/changes.rst
- patsy/build.py
- patsy/categorical.py
- patsy/constraint.py
- patsy/desc.py
- patsy/design_info.py
- patsy/eval.py
- patsy/highlevel.py
- patsy/mgcv_cubic_splines.py
- patsy/missing.py
- patsy/parse_formula.py
- patsy/splines.py
- patsy/test_highlevel.py
- patsy/tokens.py
- patsy/util.py
- patsy/version.py
- tox.ini


Changes:

=====================================
.github/workflows/publish.yml deleted
=====================================
@@ -1,27 +0,0 @@
-name: Publish tagged releases to PyPI
-
-on:
-  push:
-    tags:
-      - "v*"
-
-jobs:
-  deploy:
-    runs-on: ubuntu-latest
-    steps:
-    - uses: actions/checkout at v1
-    - name: Set up Python
-      uses: actions/setup-python at v1
-      with:
-        python-version: '3.7'
-    - name: Install dependencies
-      run: |
-        python -m pip install --upgrade pip
-        pip install setuptools wheel twine
-    - name: Build and publish
-      env:
-        TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
-        TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
-      run: |
-        python setup.py sdist bdist_wheel
-        twine upload dist/*


=====================================
.github/workflows/tox.yml deleted
=====================================
@@ -1,57 +0,0 @@
-name: Run Tox Tests
-
-on:
-  push:
-    branches:
-      - "*"
-  pull_request:
-    branches:
-      - "*"
-
-jobs:
-  build:
-    runs-on: ubuntu-latest
-    strategy:
-      max-parallel: 4
-      matrix:
-        python-version: ['3.6', '3.7', '3.8', '3.9', '3.10', '3.11', '3.12', '3.13']
-        pandas-presence: ['with_pandas', 'without_pandas']
-    env:
-      PYTHON_VERSION: ${{ matrix.python-version }}
-      PANDAS_PRESENCE: ${{ matrix.pandas-presence }}
-    steps:
-    - uses: actions/checkout at v2
-    - name: Set up Python ${{ matrix.python-version }}
-      id: gha-python
-      uses: actions/setup-python at v4
-      with:
-        python-version: ${{ matrix.python-version }}
-      continue-on-error: true
-    - name: Install PyEnv
-      if: ${{ steps.gha-python.outcome == 'failure' }}
-      run: |
-        curl https://pyenv.run | bash
-        PYENV_ROOT="$HOME/.pyenv"
-        echo "$PYENV_ROOT/bin" >> $GITHUB_PATH
-        echo "$PYENV_ROOT/shims" >> $GITHUB_PATH
-        echo "PYENV_ROOT=$PYENV_ROOT" >> $GITHUB_ENV
-    - name: Install Python ${{ matrix.python-version }} using PyEnv
-      if: ${{ steps.gha-python.outcome == 'failure' }}
-      run: |
-        pyenv install "${{ matrix.python-version }}"
-        pyenv local "${{ matrix.python-version }}"
-        pyenv versions
-    - name: Install dependencies
-      run: |
-        pip install -U pip
-        pip install tox tox-gh-actions
-    - name: Test with tox
-      run: |
-        PYTHON_ENV="py$(echo $PYTHON_VERSION | sed 's/\.//;s/\-dev//')"
-        tox -e "${PYTHON_ENV}-${PANDAS_PRESENCE}"
-    - name: Upload coverage to Codecov
-      uses: codecov/codecov-action at v1.0.10
-      with:
-        file: ./coverage.xml
-        flags: unittests
-        env_vars: PYTHON_VERSION,PANDAS_PRESENCE


=====================================
.pre-commit-config.yaml
=====================================
@@ -1,14 +1,12 @@
 repos:
 -   repo: https://github.com/pre-commit/pre-commit-hooks
-    rev: v5.0.0
+    rev: v6.0.0
     hooks:
     - id: check-yaml
     - id: end-of-file-fixer
     - id: trailing-whitespace
     - id: fix-byte-order-marker
     - id: destroyed-symlinks
-    - id: fix-encoding-pragma
-      args: ["--remove"]
     - id: mixed-line-ending
     - id: name-tests-test
       args: ["--pytest-test-first"]
@@ -17,7 +15,7 @@ repos:
       exclude: ".ipynb"
 
 - repo: https://github.com/astral-sh/ruff-pre-commit
-  rev: v0.7.3
+  rev: v0.13.2
   hooks:
     - id: ruff-format
       types_or: [ python, pyi, jupyter ]


=====================================
README.md
=====================================
@@ -20,7 +20,7 @@ building design matrices. Patsy brings the convenience of [R](http://www.r-proje
 ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/patsy.svg)
 ![https://patsy.readthedocs.io/](https://img.shields.io/badge/docs-read%20now-blue.svg)
 ![PyPI - Status](https://img.shields.io/pypi/status/patsy.svg)
-![https://coveralls.io/r/pydata/patsy?branch=master](https://coveralls.io/repos/pydata/patsy/badge.png?branch=master)
+[![https://coveralls.io/r/pydata/patsy?branch=master](https://coveralls.io/repos/pydata/patsy/badge.png?branch=master)](https://coveralls.io/github/pydata/patsy)
 ![https://doi.org/10.5281/zenodo.592075](https://zenodo.org/badge/DOI/10.5281/zenodo.592075.svg)
 
 - **Documentation:** <https://patsy.readthedocs.io/>


=====================================
doc/changes.rst
=====================================
@@ -8,6 +8,11 @@ All Patsy releases are archived at Zenodo:
 .. image:: https://zenodo.org/badge/DOI/10.5281/zenodo.592075.svg
    :target: https://doi.org/10.5281/zenodo.592075
 
+v1.0.2
+------
+
+* Fixed compatibility with Pandas 3's new `StringDtype`.
+
 v1.0.1
 ------
 


=====================================
patsy/build.py
=====================================
@@ -285,7 +285,7 @@ def _build_subterm(subterm, factor_infos, factor_values, out):
                 contrast = subterm.contrast_matrices[factor]
                 if np.any(factor_values[factor] < 0):
                     raise PatsyError(
-                        "can't build a design matrix " "containing missing values",
+                        "can't build a design matrix containing missing values",
                         factor,
                     )
                 out[:, i] *= contrast.matrix[factor_values[factor], column_idx]
@@ -929,9 +929,7 @@ def build_design_matrices(
     if isinstance(NA_action, str):
         NA_action = NAAction(NA_action)
     if return_type == "dataframe" and not have_pandas:
-        raise PatsyError(
-            "pandas.DataFrame was requested, but pandas " "is not installed"
-        )
+        raise PatsyError("pandas.DataFrame was requested, but pandas is not installed")
     if return_type not in ("matrix", "dataframe"):
         raise PatsyError(
             "unrecognized output type %r, should be "


=====================================
patsy/categorical.py
=====================================
@@ -354,7 +354,7 @@ def categorical_to_int(data, levels, NA_action, origin=None):
         level_to_int = dict(zip(levels, range(len(levels))))
     except TypeError:
         raise PatsyError(
-            "Error interpreting categorical data: " "all items must be hashable", origin
+            "Error interpreting categorical data: all items must be hashable", origin
         )
 
     # fastpath to avoid doing an item-by-item iteration over boolean arrays,


=====================================
patsy/constraint.py
=====================================
@@ -314,7 +314,7 @@ class _EvalConstraint(object):
         right = self.eval(tree.args[1])
         if not self.is_constant(right):
             raise PatsyError(
-                "Can't divide by a variable in a linear " "constraint", tree.args[1]
+                "Can't divide by a variable in a linear constraint", tree.args[1]
             )
         return left / right[-1]
 
@@ -327,7 +327,7 @@ class _EvalConstraint(object):
             return left * right[-1]
         else:
             raise PatsyError(
-                "Can't multiply one variable by another " "in a linear constraint", tree
+                "Can't multiply one variable by another in a linear constraint", tree
             )
 
     def _eval_binary_eq(self, tree):


=====================================
patsy/desc.py
=====================================
@@ -298,7 +298,7 @@ def _eval_binary_minus(evaluator, tree):
 def _check_interactable(expr):
     if expr.intercept:
         raise PatsyError(
-            "intercept term cannot interact with " "anything else",
+            "intercept term cannot interact with anything else",
             expr.intercept_origin,
         )
 
@@ -392,7 +392,7 @@ def _eval_one(evaluator, tree):
 
 
 def _eval_number(evaluator, tree):
-    raise PatsyError("numbers besides '0' and '1' are " "only allowed with **", tree)
+    raise PatsyError("numbers besides '0' and '1' are only allowed with **", tree)
 
 
 def _eval_python_expr(evaluator, tree):
@@ -437,14 +437,14 @@ class Evaluator(object):
         key = (tree.type, len(tree.args))
         if key not in self._evaluators:
             raise PatsyError(
-                "I don't know how to evaluate this " "'%s' operator" % (tree.type,),
+                "I don't know how to evaluate this '%s' operator" % (tree.type,),
                 tree.token,
             )
         result = self._evaluators[key](self, tree)
         if require_evalexpr and not isinstance(result, IntermediateExpr):
             if isinstance(result, ModelDesc):
                 raise PatsyError(
-                    "~ can only be used once, and " "only at the top level", tree
+                    "~ can only be used once, and only at the top level", tree
                 )
             else:
                 raise PatsyError(


=====================================
patsy/design_info.py
=====================================
@@ -88,14 +88,14 @@ class FactorInfo:
         if self.type == "numerical":
             if not isinstance(num_columns, int):
                 raise ValueError(
-                    "For numerical factors, num_columns " "must be an integer"
+                    "For numerical factors, num_columns must be an integer"
                 )
             if categories is not None:
-                raise ValueError("For numerical factors, categories " "must be None")
+                raise ValueError("For numerical factors, categories must be None")
         else:
             assert self.type == "categorical"
             if num_columns is not None:
-                raise ValueError("For categorical factors, num_columns " "must be None")
+                raise ValueError("For categorical factors, num_columns must be None")
             categories = tuple(categories)
         self.num_columns = num_columns
         self.categories = categories
@@ -280,8 +280,7 @@ class DesignInfo(object):
 
         if (factor_infos is None) != (term_codings is None):
             raise ValueError(
-                "Must specify either both or neither of "
-                "factor_infos= and term_codings="
+                "Must specify either both or neither of factor_infos= and term_codings="
             )
 
         self.factor_infos = factor_infos
@@ -304,7 +303,7 @@ class DesignInfo(object):
                 term_factors = set(term.factors)
                 for subterm in subterms:
                     if not isinstance(subterm, SubtermInfo):
-                        raise ValueError("expected SubtermInfo, " "not %r" % (subterm,))
+                        raise ValueError("expected SubtermInfo, not %r" % (subterm,))
                     if not term_factors.issuperset(subterm.factors):
                         raise ValueError("unexpected factors in subterm")
 
@@ -312,9 +311,7 @@ class DesignInfo(object):
             for term in self.term_codings:
                 all_factors.update(term.factors)
             if all_factors != set(self.factor_infos):
-                raise ValueError(
-                    "Provided Term objects and factor_infos " "do not match"
-                )
+                raise ValueError("Provided Term objects and factor_infos do not match")
             for factor, factor_info in self.factor_infos.items():
                 if not isinstance(factor_info, FactorInfo):
                     raise ValueError(
@@ -343,8 +340,7 @@ class DesignInfo(object):
                             exp_cols *= cm.shape[1]
                     if cat_factors != set(subterm.contrast_matrices):
                         raise ValueError(
-                            "Mismatch between contrast_matrices "
-                            "and categorical factors"
+                            "Mismatch between contrast_matrices and categorical factors"
                         )
                     if exp_cols != subterm.num_columns:
                         raise ValueError("Unexpected num_columns")
@@ -368,7 +364,7 @@ class DesignInfo(object):
                 idx += term_columns
             if idx != len(self.column_names):
                 raise ValueError(
-                    "mismatch between column_names and columns " "coded by given terms"
+                    "mismatch between column_names and columns coded by given terms"
                 )
             self.term_name_slices = OrderedDict(
                 [(term.name(), slice_) for (term, slice_) in self.term_slices.items()]


=====================================
patsy/eval.py
=====================================
@@ -526,7 +526,7 @@ class EvalFactor(object):
         # original code
         if has_bare_variable_reference(state["transforms"], self.code):
             raise PatsyError(
-                "names of this form are reserved for " "internal use (%s)" % (token,),
+                "names of this form are reserved for internal use (%s)" % (token,),
                 token.origin,
             )
         # Pull out all the '_patsy_stobj0__center__.transform(x)' pieces


=====================================
patsy/highlevel.py
=====================================
@@ -107,7 +107,7 @@ def incr_dbuilder(formula_like, data_iter_maker, eval_env=0, NA_action="drop"):
         raise PatsyError("bad formula-like object")
     if len(design_infos[0].column_names) > 0:
         raise PatsyError(
-            "encountered outcome variables for a model " "that does not expect them"
+            "encountered outcome variables for a model that does not expect them"
         )
     return design_infos[1]
 
@@ -149,9 +149,7 @@ def incr_dbuilders(formula_like, data_iter_maker, eval_env=0, NA_action="drop"):
 #   any object with a special method __patsy_get_model_desc__
 def _do_highlevel_design(formula_like, data, eval_env, NA_action, return_type):
     if return_type == "dataframe" and not have_pandas:
-        raise PatsyError(
-            "pandas.DataFrame was requested, but pandas " "is not installed"
-        )
+        raise PatsyError("pandas.DataFrame was requested, but pandas is not installed")
     if return_type not in ("matrix", "dataframe"):
         raise PatsyError(
             "unrecognized output type %r, should be "
@@ -219,7 +217,7 @@ def _do_highlevel_design(formula_like, data, eval_env, NA_action, return_type):
         if rhs_orig_index is not None and lhs_orig_index is not None:
             if not rhs_orig_index.equals(lhs_orig_index):
                 raise PatsyError(
-                    "index mismatch: outcome and " "predictor have incompatible indexes"
+                    "index mismatch: outcome and predictor have incompatible indexes"
                 )
         if return_type == "dataframe":
             if rhs_orig_index is not None and lhs_orig_index is None:
@@ -298,7 +296,7 @@ def dmatrix(formula_like, data={}, eval_env=0, NA_action="drop", return_type="ma
     )
     if lhs.shape[1] != 0:
         raise PatsyError(
-            "encountered outcome variables for a model " "that does not expect them"
+            "encountered outcome variables for a model that does not expect them"
         )
     return rhs
 


=====================================
patsy/mgcv_cubic_splines.py
=====================================
@@ -161,7 +161,7 @@ def _row_tensor_product(dms):
     for dm in dms:
         if dm.shape[0] != tp_nrows:
             raise ValueError(
-                "Tensor product arguments should have " "same number of rows."
+                "Tensor product arguments should have same number of rows."
             )
         tp_ncols *= dm.shape[1]
     tp = np.zeros((tp_nrows, tp_ncols))
@@ -624,7 +624,7 @@ class CubicRegressionSpline(object):
             x = x[:, 0]
         if x.ndim > 1:
             raise ValueError(
-                "Input to %r must be 1-d, " "or a 2-d column vector." % (self._name,)
+                "Input to %r must be 1-d, or a 2-d column vector." % (self._name,)
             )
 
         self._tmp.setdefault("xs", []).append(x)
@@ -649,7 +649,7 @@ class CubicRegressionSpline(object):
             else:
                 constraints = np.atleast_2d(constraints)
                 if constraints.ndim != 2:
-                    raise ValueError("Constraints must be 2-d array or " "1-d vector.")
+                    raise ValueError("Constraints must be 2-d array or 1-d vector.")
                 n_constraints = constraints.shape[0]
 
         n_inner_knots = None
@@ -704,7 +704,7 @@ class CubicRegressionSpline(object):
             x = x[:, 0]
         if x.ndim > 1:
             raise ValueError(
-                "Input to %r must be 1-d, " "or a 2-d column vector." % (self._name,)
+                "Input to %r must be 1-d, or a 2-d column vector." % (self._name,)
             )
         dm = _get_crs_dmatrix(
             x, self._all_knots, self._constraints, cyclic=self._cyclic
@@ -982,7 +982,7 @@ class TE(object):
             else:
                 constraints = np.atleast_2d(constraints)
                 if constraints.ndim != 2:
-                    raise ValueError("Constraints must be 2-d array or " "1-d vector.")
+                    raise ValueError("Constraints must be 2-d array or 1-d vector.")
 
         self._constraints = constraints
 
@@ -992,7 +992,7 @@ class TE(object):
             arg = atleast_2d_column_default(arg)
             if arg.ndim != 2:
                 raise ValueError(
-                    "Each tensor product argument must be " "a 2-d array or 1-d vector."
+                    "Each tensor product argument must be a 2-d array or 1-d vector."
                 )
             args_2d.append(arg)
 
@@ -1190,7 +1190,7 @@ def test_te_2smooths():
     assert np.allclose(dmatrix_nocons, dmatrix_R_nocons, rtol=1e-12, atol=0.0)
 
     builder = incr_dbuilder(
-        "te(cr(x1, df=5), cc(x2, df=6), " "constraints='center') - 1",
+        "te(cr(x1, df=5), cc(x2, df=6), constraints='center') - 1",
         lambda: iter(data_chunked),
     )
     dmatrix_cons = build_design_matrices([builder], new_data)[0]


=====================================
patsy/missing.py
=====================================
@@ -183,7 +183,7 @@ class NAAction(object):
             total_mask |= is_NA
         good_mask = ~total_mask
         # "..." to handle 1- versus 2-dim indexing
-        return [v[good_mask, ...] for v in values]
+        return [v[good_mask] if v.ndim == 1 else v[good_mask, ...] for v in values]
 
     __getstate__ = no_pickling
 


=====================================
patsy/parse_formula.py
=====================================
@@ -69,7 +69,7 @@ def _read_python_expr(it, end_tokens):
         return Token(token_type, Origin.combine(origins), extra=expr_text)
     else:
         raise PatsyError(
-            "unclosed bracket in embedded Python " "expression", Origin.combine(origins)
+            "unclosed bracket in embedded Python expression", Origin.combine(origins)
         )
 
 


=====================================
patsy/splines.py
=====================================
@@ -171,7 +171,7 @@ class BS(object):
         if x.ndim == 2 and x.shape[1] == 1:
             x = x[:, 0]
         if x.ndim > 1:
-            raise ValueError("input to 'bs' must be 1-d, " "or a 2-d column vector")
+            raise ValueError("input to 'bs' must be 1-d, or a 2-d column vector")
         # There's no better way to compute exact quantiles than memorizing
         # all data.
         self._tmp.setdefault("xs", []).append(x)


=====================================
patsy/test_highlevel.py
=====================================
@@ -979,3 +979,21 @@ def test_C_and_pandas_categorical():
         assert np.allclose(
             dmatrix("C(obj, levels=['a', 'b'])", d), [[1, 0], [1, 1], [1, 0]]
         )
+
+
+def test_NAActioon_pandas_string_index():
+    if not have_pandas:
+        return
+    from patsy.missing import NAAction
+
+    formula = "1 + x + z"
+    action = NAAction("drop")
+    data = pandas.DataFrame(
+        {"z": [1.0, np.nan, 2.0], "x": [1, 2, 3]}, index=["a", "b", "c"]
+    )
+    dm = dmatrix(formula, data, 0, NA_action=action, return_type="dataframe")
+    di = dm.design_info
+    data2 = pandas.DataFrame({"z": [4.0, 5.0], "x": [6, 7]})
+    dm2 = dmatrix(di, data2, 0, return_type="dataframe")
+    assert np.allclose(dm2, [[1.0, 6.0, 4.0], [1.0, 7.0, 5.0]])
+    assert list(dm2.columns) == ["Intercept", "x", "z"]


=====================================
patsy/tokens.py
=====================================
@@ -38,7 +38,7 @@ def python_tokenize(code):
             origin = Origin(code, start, end)
             if pytype == tokenize.ERRORTOKEN:
                 raise PatsyError(
-                    "error tokenizing input " "(maybe an unclosed string?)", origin
+                    "error tokenizing input (maybe an unclosed string?)", origin
                 )
             if pytype == tokenize.COMMENT:
                 raise PatsyError("comments are not allowed", origin)


=====================================
patsy/util.py
=====================================
@@ -40,14 +40,19 @@ from .compat import optional_dep_ok
 try:
     import pandas
 except ImportError:
-    have_pandas = False
+    PANDAS3 = have_pandas = False
 else:
     have_pandas = True
+    import packaging.version
+
+    pandas_version = packaging.version.parse(pandas.__version__)
+    PANDAS3 = pandas_version >= packaging.version.parse("3.0.0.dev0")
 
 # Pandas versions < 0.9.0 don't have Categorical
 # Can drop this guard whenever we drop support for such older versions of
 # pandas.
 have_pandas_categorical = have_pandas and hasattr(pandas, "Categorical")
+have_pandas_string_dtype = have_pandas and hasattr(pandas, "StringDtype")
 if not have_pandas:
     _pandas_is_categorical_dtype = None
 else:
@@ -65,6 +70,11 @@ else:
         )
 have_pandas_categorical_dtype = _pandas_is_categorical_dtype is not None
 
+
+def safe_is_pandas_string_dtype(x):
+    return have_pandas_string_dtype and isinstance(x, pandas.StringDtype)
+
+
 # The handling of the `copy` keyword has been changed since numpy>=2.
 # https://numpy.org/devdocs/numpy_2_0_migration_guide.html#adapting-to-changes-in-the-copy-keyword
 # If numpy<2 support is dropped, this try-clause can be removed.
@@ -118,7 +128,9 @@ def test_asarray_or_pandas():
         assert s_view1.name == "A"
         assert np.array_equal(s_view1.index, [10, 20, 30])
         s_view1[10] = 101
-        assert s[10] == 101
+        # pandas 3 uses copy-on-write, so no longer valid
+        if not PANDAS3:
+            assert s[10] == 101
         s_copy = asarray_or_pandas(s, copy=True)
         assert s_copy.name == "A"
         assert np.array_equal(s_copy.index, [10, 20, 30])
@@ -130,14 +142,18 @@ def test_asarray_or_pandas():
         assert s_view2.name == "A"
         assert np.array_equal(s_view2.index, [10, 20, 30])
         s_view2[10] = 99
-        assert s[10] == 99
+        # pandas 3 uses copy-on-write, so no longer valid
+        if not PANDAS3:
+            assert s[10] == 99
 
         df = pandas.DataFrame([[1, 2, 3]], columns=["A", "B", "C"], index=[10])
         df_view1 = asarray_or_pandas(df)
         df_view1.loc[10, "A"] = 101
         assert np.array_equal(df_view1.columns, ["A", "B", "C"])
         assert np.array_equal(df_view1.index, [10])
-        assert df.loc[10, "A"] == 101
+        # pandas 3 uses copy-on-write, so no longer valid
+        if not PANDAS3:
+            assert df.loc[10, "A"] == 101
         df_copy = asarray_or_pandas(df, copy=True)
         assert np.array_equal(df_copy, df)
         assert np.array_equal(df_copy.columns, ["A", "B", "C"])
@@ -799,7 +815,8 @@ def test_safe_is_pandas_categorical():
 #   https://github.com/pydata/pandas/issues/9581
 #   https://github.com/pydata/pandas/issues/9581#issuecomment-77099564
 def safe_issubdtype(dt1, dt2):
-    if safe_is_pandas_categorical_dtype(dt1):
+    # The second condition is needed to support pandas >= 3 (!)
+    if safe_is_pandas_categorical_dtype(dt1) or safe_is_pandas_string_dtype(dt1):
         return False
     return np.issubdtype(dt1, dt2)
 


=====================================
patsy/version.py
=====================================
@@ -6,15 +6,15 @@
 # places -- it is imported by patsy/__init__.py, execfile'd by setup.py, etc.
 
 # We use a simple scheme:
-#   1.0.1 -> 1.0.1+dev -> 1.1.0 -> 1.1.0+dev
+#   1.0.2 -> 1.0.2+dev -> 1.1.0 -> 1.1.0+dev
 # where the +dev versions are never released into the wild, they're just what
 # we stick into the VCS in between releases.
 #
 # This is compatible with PEP 440:
 #   http://legacy.python.org/dev/peps/pep-0440/
 # via the use of the "local suffix" "+dev", which is disallowed on index
-# servers and causes 1.0.1+dev to sort after plain 1.0.1, which is what we
-# want. (Contrast with the special suffix 1.0.1.dev, which sorts *before*
-# 1.0.1.)
+# servers and causes 1.0.2+dev to sort after plain 1.0.2, which is what we
+# want. (Contrast with the special suffix 1.0.2.dev, which sorts *before*
+# 1.0.2.)
 
-__version__ = "1.0.1"
+__version__ = "1.0.2"


=====================================
tox.ini
=====================================
@@ -1,5 +1,5 @@
 [tox]
-envlist = {py36,py37,py38,py39,py310,py311,py312,py313}-{with_pandas,without_pandas}
+envlist = {py36,py37,py38,py39,py310,py311,py312,py313,py314}-{with_pandas,without_pandas}
 
 [gh-actions]
 python =
@@ -11,6 +11,7 @@ python =
   3.11: py311
   3.12: py312
   3.13: py313
+  3.14: py314
 
 [testenv]
 deps=



View it on GitLab: https://salsa.debian.org/med-team/patsy/-/commit/e40f1ec51476f4fdeeebc8d5712be3fc55873af2

-- 
View it on GitLab: https://salsa.debian.org/med-team/patsy/-/commit/e40f1ec51476f4fdeeebc8d5712be3fc55873af2
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/debian-med-commit/attachments/20251025/cc8de50f/attachment-0001.htm>


More information about the debian-med-commit mailing list