diff --git a/ci/azure_template_posix.yml b/ci/azure_template_posix.yml index 38c68e9e4a..49db563b8b 100644 --- a/ci/azure_template_posix.yml +++ b/ci/azure_template_posix.yml @@ -152,9 +152,8 @@ jobs: testRunTitle: 'Python $(python.version)' condition: succeededOrFailed() - - task: PublishCodeCoverageResults@1 + - task: PublishCodeCoverageResults@2 inputs: - codeCoverageTool: Cobertura summaryFileLocation: '$(System.DefaultWorkingDirectory)/**/coverage.xml' condition: and(eq(variables['coverage'], 'true'), ne(variables['test.install'], 'true')) diff --git a/doc/source/conf.py b/doc/source/conf.py index 73d9569621..e234d4bf16 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -10,7 +10,6 @@ import glob import hashlib import os -from typing import Dict, List from packaging.version import parse diff --git a/examples/system_formulas.ipynb b/examples/system_formulas.ipynb index 701661579c..71648277a1 100644 --- a/examples/system_formulas.ipynb +++ b/examples/system_formulas.ipynb @@ -46,9 +46,9 @@ "from collections import OrderedDict\n", "\n", "formula = OrderedDict()\n", - "formula[\n", - " \"benefits\"\n", - "] = \"hrbens ~ educ + exper + expersq + union + south + nrtheast + nrthcen + male\"\n", + "formula[\"benefits\"] = (\n", + " \"hrbens ~ educ + exper + expersq + union + south + nrtheast + nrthcen + male\"\n", + ")\n", "formula[\"earnings\"] = \"hrearn ~ educ + exper + expersq + nrtheast + married + male\"" ] }, diff --git a/linearmodels/asset_pricing/covariance.py b/linearmodels/asset_pricing/covariance.py index b629cec928..0bdb03ff3d 100644 --- a/linearmodels/asset_pricing/covariance.py +++ b/linearmodels/asset_pricing/covariance.py @@ -1,6 +1,7 @@ """ Covariance estimators for linear factor models """ + from __future__ import annotations from numpy import empty, ndarray diff --git a/linearmodels/asset_pricing/model.py b/linearmodels/asset_pricing/model.py index 201518800c..97319c5bec 100644 --- a/linearmodels/asset_pricing/model.py +++ b/linearmodels/asset_pricing/model.py @@ -1,6 +1,7 @@ """ Linear factor models for applications in asset pricing """ + from __future__ import annotations from typing import Any, Callable, cast diff --git a/linearmodels/asset_pricing/results.py b/linearmodels/asset_pricing/results.py index 23f05d3596..ae9eac75d1 100644 --- a/linearmodels/asset_pricing/results.py +++ b/linearmodels/asset_pricing/results.py @@ -1,6 +1,7 @@ """ Results for linear factor models """ + from __future__ import annotations from linearmodels.compat.statsmodels import Summary diff --git a/linearmodels/compat/formulaic.py b/linearmodels/compat/formulaic.py new file mode 100644 index 0000000000..23020279ca --- /dev/null +++ b/linearmodels/compat/formulaic.py @@ -0,0 +1,12 @@ +def monkey_patch_materializers(): + from formulaic.materializers.base import FormulaMaterializer + from formulaic.materializers.pandas import PandasMaterializer + + if "pandas.DataFrame" not in FormulaMaterializer.REGISTERED_INPUTS: + FormulaMaterializer.REGISTERED_INPUTS["pandas.DataFrame"] = ( + FormulaMaterializer.REGISTERED_INPUTS["pandas.core.frame.DataFrame"] + ) + if "pandas.DataFrame" not in PandasMaterializer.REGISTERED_INPUTS: + PandasMaterializer.REGISTERED_INPUTS["pandas.DataFrame"] = ( + PandasMaterializer.REGISTERED_INPUTS["pandas.core.frame.DataFrame"] + ) diff --git a/linearmodels/iv/_utility.py b/linearmodels/iv/_utility.py index c7096e32cc..67438f99f2 100644 --- a/linearmodels/iv/_utility.py +++ b/linearmodels/iv/_utility.py @@ -12,6 +12,11 @@ from linearmodels.typing import Float64Array +from ..compat.formulaic import monkey_patch_materializers + +# Monkey patch parsers if needed, remove once formulaic updated +monkey_patch_materializers() + PARSING_ERROR = """ Conversion of formula blocks to DataFrames failed. The formula blocks used for conversion were: diff --git a/linearmodels/iv/absorbing.py b/linearmodels/iv/absorbing.py index b6f82ad61a..7b945af20f 100644 --- a/linearmodels/iv/absorbing.py +++ b/linearmodels/iv/absorbing.py @@ -125,9 +125,9 @@ def lsmr_annihilate( return empty_like(y) use_cache = use_cache and x_hash is not None regressor_hash = x_hash if x_hash is not None else "" - default_opts: dict[ - str, bool | float | str | ArrayLike | None | dict[str, Any] - ] = dict(atol=1e-8, btol=1e-8, show=False) + default_opts: dict[str, bool | float | str | ArrayLike | None | dict[str, Any]] = ( + dict(atol=1e-8, btol=1e-8, show=False) + ) assert lsmr_options is not None default_opts.update(lsmr_options) resids = [] @@ -835,8 +835,9 @@ def _prepare_interactions(self) -> None: def _first_time_fit( self, use_cache: bool, - absorb_options: None - | (dict[str, bool | float | str | ArrayLike | None | dict[str, Any]]), + absorb_options: None | ( + dict[str, bool | float | str | ArrayLike | None | dict[str, Any]] + ), method: str, ) -> None: weights = ( @@ -947,8 +948,9 @@ def fit( cov_type: str = "robust", debiased: bool = False, method: str = "auto", - absorb_options: None - | (dict[str, bool | float | str | ArrayLike | None | dict[str, Any]]) = None, + absorb_options: None | ( + dict[str, bool | float | str | ArrayLike | None | dict[str, Any]] + ) = None, use_cache: bool = True, lsmr_options: dict[str, float | bool] | None = None, **cov_config: Any, diff --git a/linearmodels/iv/model.py b/linearmodels/iv/model.py index 74102a2228..2d1c722c72 100644 --- a/linearmodels/iv/model.py +++ b/linearmodels/iv/model.py @@ -1,6 +1,7 @@ """ Instrumental variable estimators """ + from __future__ import annotations from typing import Any, TypeVar, Union, cast diff --git a/linearmodels/iv/results.py b/linearmodels/iv/results.py index bde9620d23..2ee79b01ad 100644 --- a/linearmodels/iv/results.py +++ b/linearmodels/iv/results.py @@ -1,6 +1,7 @@ """ Results containers and post-estimation diagnostics for IV models """ + from __future__ import annotations from linearmodels.compat.statsmodels import Summary diff --git a/linearmodels/panel/covariance.py b/linearmodels/panel/covariance.py index 0f2fae6e96..05a9cef3c5 100644 --- a/linearmodels/panel/covariance.py +++ b/linearmodels/panel/covariance.py @@ -79,6 +79,7 @@ class HomoskedasticCovariance: where df is ``extra_df`` and n-df is replace by n-df-k if ``debiased`` is ``True``. """ + ALLOWED_KWARGS: tuple[str, ...] = tuple() DEFAULT_KERNEL = "newey-west" @@ -270,6 +271,7 @@ class ClusteredCovariance(HomoskedasticCovariance): where g is the number of distinct groups and n is the number of observations. """ + ALLOWED_KWARGS = ("clusters", "group_debias") def __init__( @@ -400,6 +402,7 @@ class DriscollKraay(HomoskedasticCovariance): where df is ``extra_df`` and n-df is replace by n-df-k if ``debiased`` is ``True``. :math:`K(i, bw)` is the kernel weighting function. """ + ALLOWED_KWARGS = ("kernel", "bandwidth") # TODO: Test @@ -517,6 +520,7 @@ class ACCovariance(HomoskedasticCovariance): where df is ``extra_df`` and n-df is replace by n-df-k if ``debiased`` is ``True``. :math:`K(i, bw)` is the kernel weighting function. """ + ALLOWED_KWARGS = ("kernel", "bandwidth") # TODO: Docstring diff --git a/linearmodels/panel/data.py b/linearmodels/panel/data.py index 153bdff27f..6ea1135731 100644 --- a/linearmodels/panel/data.py +++ b/linearmodels/panel/data.py @@ -254,7 +254,7 @@ def __init__( raise TypeError("Only ndarrays, DataFrames or DataArrays are " "supported") if convert_dummies: self._frame = expand_categoricals(self._frame, drop_first) - self._frame = self._frame.astype(np.float64, copy=False) + self._frame = self._frame.astype(np.float64) time_index = Series(self.index.levels[1]) if not ( @@ -519,32 +519,29 @@ def demean_pass( return PanelData(current) @overload - def demean( + def demean( # noqa: E704 self, group: Literal["entity", "time", "both"], *, return_panel: Literal[False], - ) -> Float64Array: - ... + ) -> Float64Array: ... @overload - def demean( + def demean( # noqa: E704 self, group: Literal["entity", "time", "both"] = ..., weights: PanelData | None = ..., return_panel: Literal[True] = ..., low_memory: bool = ..., - ) -> PanelData: - ... + ) -> PanelData: ... @overload - def demean( + def demean( # noqa: E704 self, group: Literal["entity", "time", "both"], weights: PanelData | None, return_panel: Literal[False], - ) -> Float64Array: - ... + ) -> Float64Array: ... # noqa: E704 def demean( self, @@ -757,7 +754,7 @@ def dummies(self, group: str = "entity", drop_first: bool = False) -> DataFrame: cols = self.entities if group == "entity" else self.time # TODO: Incorrect typing in pandas-stubs not handling Hashable | None dummy_cols = [c for c in cols if c in dummies] - return dummies[dummy_cols].astype(np.float64, copy=False) # type: ignore + return dummies[dummy_cols].astype(np.float64) # type: ignore PanelDataLike = Union[PanelData, ArrayLike] diff --git a/linearmodels/panel/model.py b/linearmodels/panel/model.py index 15539992fb..438896b4bf 100644 --- a/linearmodels/panel/model.py +++ b/linearmodels/panel/model.py @@ -1,5 +1,7 @@ from __future__ import annotations +from linearmodels.compat.formulaic import monkey_patch_materializers + from collections.abc import Mapping from typing import Any, NamedTuple, Union, cast @@ -61,6 +63,9 @@ NumericArray, ) +# Monkey patch parsers if needed, remove once formulaic updated +monkey_patch_materializers() + CovarianceEstimator = Union[ ACCovariance, ClusteredCovariance, diff --git a/linearmodels/shared/hypotheses.py b/linearmodels/shared/hypotheses.py index 05b61c59b6..64ac2e8770 100644 --- a/linearmodels/shared/hypotheses.py +++ b/linearmodels/shared/hypotheses.py @@ -1,5 +1,7 @@ from __future__ import annotations +from linearmodels.compat.formulaic import monkey_patch_materializers + from collections.abc import Mapping from formulaic.utils.constraints import LinearConstraints @@ -9,6 +11,9 @@ from linearmodels.typing import ArrayLike +# Monkey patch parsers if needed, remove once formulaic updated +monkey_patch_materializers() + class WaldTestStatistic: """ diff --git a/linearmodels/shared/utility.py b/linearmodels/shared/utility.py index 2c8840ee3b..c65a3193d6 100644 --- a/linearmodels/shared/utility.py +++ b/linearmodels/shared/utility.py @@ -23,11 +23,9 @@ class SupportsKeysAndGetItem(Protocol[_KT, _VT_co]): - def keys(self) -> Iterable[_KT]: - ... + def keys(self) -> Iterable[_KT]: ... # noqa: E704 - def __getitem__(self, __k: _KT) -> _VT_co: - ... + def __getitem__(self, __k: _KT) -> _VT_co: ... # noqa: E704 def _new_attr_dict_(*args: Iterable[tuple[Any, Any]]) -> AttrDict: diff --git a/linearmodels/system/gmm.py b/linearmodels/system/gmm.py index be43a02f43..e1561eb1f8 100644 --- a/linearmodels/system/gmm.py +++ b/linearmodels/system/gmm.py @@ -1,6 +1,7 @@ """ Covariance and weight estimation for GMM IV estimators """ + from __future__ import annotations from collections.abc import Sequence diff --git a/linearmodels/system/model.py b/linearmodels/system/model.py index da5fdb731d..27d775a9de 100644 --- a/linearmodels/system/model.py +++ b/linearmodels/system/model.py @@ -11,8 +11,11 @@ Systems of Simultaneous Equations in R. Journal of Statistical Software, 23(4), 1 - 40. doi:http://dx.doi.org/10.18637/jss.v023.i04 """ + from __future__ import annotations +from linearmodels.compat.formulaic import monkey_patch_materializers + from collections.abc import Mapping, Sequence from functools import reduce import textwrap @@ -57,6 +60,9 @@ __all__ = ["SUR", "IV3SLS", "IVSystemGMM", "LinearConstraint"] +# Monkey patch parsers if needed, remove once formulaic updated +monkey_patch_materializers() + UNKNOWN_EQ_TYPE = """ Contents of each equation must be either a dictionary with keys "dependent" and "exog" or a 2-element tuple of he form (dependent, exog). @@ -877,8 +883,7 @@ def _common_indiv_results( constant: bool, total_ss: float, *, - weight_est: None - | ( + weight_est: None | ( HomoskedasticWeightMatrix | HeteroskedasticWeightMatrix | KernelWeightMatrix ) = None, ) -> AttrDict: diff --git a/linearmodels/tests/iv/results/simulated-test-data.py b/linearmodels/tests/iv/results/simulated-test-data.py index a665883fe2..141902097a 100644 --- a/linearmodels/tests/iv/results/simulated-test-data.py +++ b/linearmodels/tests/iv/results/simulated-test-data.py @@ -12,6 +12,7 @@ Will also test other configurations - small, covariance-estimators, constant """ + import numpy as np from numpy.random import multivariate_normal, seed import pandas as pd diff --git a/linearmodels/tests/system/results/execute-stata-3sls.py b/linearmodels/tests/system/results/execute-stata-3sls.py index dd7c65d3d0..97568adb93 100644 --- a/linearmodels/tests/system/results/execute-stata-3sls.py +++ b/linearmodels/tests/system/results/execute-stata-3sls.py @@ -2,6 +2,7 @@ Important cases """ + import os import subprocess diff --git a/linearmodels/tests/system/results/execute-stata.py b/linearmodels/tests/system/results/execute-stata.py index 42d882d7cd..5e1c77b02d 100644 --- a/linearmodels/tests/system/results/execute-stata.py +++ b/linearmodels/tests/system/results/execute-stata.py @@ -5,6 +5,7 @@ 2. Small sample adjustment 3. Constraints across equations """ + import os import subprocess diff --git a/linearmodels/typing/__init__.py b/linearmodels/typing/__init__.py index fa9abe18a7..b7d36e229a 100644 --- a/linearmodels/typing/__init__.py +++ b/linearmodels/typing/__init__.py @@ -48,6 +48,6 @@ np.ndarray[Any, np.dtype[np.floating[Any]]], # pragma: no cover ] # pragma: no cover else: - IntArray = ( - Float64Array - ) = Int64Array = Int32Array = BoolArray = AnyArray = NumericArray = np.ndarray + IntArray = Float64Array = Int64Array = Int32Array = BoolArray = AnyArray = ( + NumericArray + ) = np.ndarray diff --git a/linearmodels/typing/data.py b/linearmodels/typing/data.py index 8cf3e0d04e..37996db3ed 100644 --- a/linearmodels/typing/data.py +++ b/linearmodels/typing/data.py @@ -29,9 +29,9 @@ AnyArray = np.ndarray[Any, Any] # pragma: no cover Uint32Array = np.ndarray[Any, np.dtype[np.uint32]] # pragma: no cover else: - Uint32Array = ( - IntArray - ) = Float64Array = Int64Array = Int32Array = BoolArray = AnyArray = NDArray + Uint32Array = IntArray = Float64Array = Int64Array = Int32Array = BoolArray = ( + AnyArray + ) = NDArray __all__ = [ "Float64Array", diff --git a/setup.py b/setup.py index 5056e001e8..76ad98dac5 100644 --- a/setup.py +++ b/setup.py @@ -1,10 +1,9 @@ from setuptools import Extension, find_namespace_packages, setup from setuptools.dist import Distribution +from setuptools.errors import CCompilerError, ExecError, PlatformError -from distutils.errors import CCompilerError, DistutilsExecError, DistutilsPlatformError import glob import os -from typing import Dict try: from Cython.Build import cythonize @@ -136,8 +135,8 @@ def run_setup(binary: bool = True) -> None: run_setup(binary=build_binary) except ( CCompilerError, - DistutilsExecError, - DistutilsPlatformError, + ExecError, + PlatformError, OSError, ValueError, ImportError,