content: # Reference: https://packaging.python.org/en/latest/guides/writing-pyproject-toml
  [build-system]
  requires = ["hatchling"]
  build-backend = "hatchling.build"
  
  [project]
  name = "narwhals"
  version = "2.8.0"
  dependencies = []
  requires-python = ">=3.9"
  authors = [
    { name = "Marco Gorelli", email = "hello_narwhals@proton.me" },
  ]
  description = "Extremely lightweight compatibility layer between dataframe libraries"
  readme = "README.md"
  keywords = ["dataframes", "interoperability", "pandas", "polars", "pyarrow", "dask", "modin", "cudf"]
  classifiers = [
    "License :: OSI Approved :: MIT License",
    "Programming Language :: Python",
    "Operating System :: OS Independent",
    "Typing :: Typed",
  ]
  
  [project.urls]
  Homepage = "https://github.com/narwhals-dev/narwhals"
  Documentation = "https://narwhals-dev.github.io/narwhals/"
  Repository = "https://github.com/narwhals-dev/narwhals"
  "Bug Tracker" = "https://github.com/narwhals-dev/narwhals/issues"
  
  [project.optional-dependencies]
  # These should be aligned with MIN_VERSIONS in narwhals/utils.py
  # Exception: modin, because `modin.__version__` isn't aligned with
  # `modin.pandas.__version__`. The latter is the one that we make
  # API decisions based on, so that's the one we track internally.
  # We have yet to determine the minimum Modin version we support
  # https://github.com/narwhals-dev/narwhals/issues/817 
  pandas = ["pandas>=1.1.3"]
  modin = ["modin"]
  cudf = ["cudf>=24.10.0"]
  pyarrow = ["pyarrow>=13.0.0"]
  pyspark = ["pyspark>=3.5.0"]
  pyspark-connect = ["pyspark[connect]>=3.5.0"]
  polars = ["polars>=0.20.4"]
  dask = ["dask[dataframe]>=2024.8"]
  duckdb = ["duckdb>=1.1"]
  ibis = ["ibis-framework>=6.0.0", "rich", "packaging", "pyarrow_hotfix"]
  sqlframe = ["sqlframe>=3.22.0,!=3.39.3"]
  
  [dependency-groups]
  core = [
    "narwhals[duckdb,pandas,polars,pyarrow,sqlframe]"
  ] 
  tests = [
    "covdefaults",
    "pytest",
    "pytest-cov",
    "pytest-env",
    "pytest-randomly",
    "pytest-xdist",
    "hypothesis",
  ]
  extra = [  # heavier dependencies we don't necessarily need in every testing job
    "scikit-learn",
  ]
  typing = [  # keep some of these pinned and bump periodically so there's fewer surprises for contributors
    "duckdb==1.3.0",
    "hypothesis",
    "pytest",
    "pandas-stubs==2.3.0.250703",
    "typing_extensions",
    "mypy~=1.15.0",
    "pyright",
    "pyarrow-stubs==19.2",
    "sqlframe",
    "polars==1.34.0",
    "uv",
    "narwhals[ibis]",
  ]
  typing-ci = [
    "narwhals[dask,modin]",
    {include-group = "typing"}
  ]
  docs = [
    "black",  # required by mkdocstrings_handlers
    "jinja2",
    "duckdb",
    "narwhals[ibis]",
    "markdown-exec[ansi]",
    "mkdocs",
    "mkdocs-autorefs",
    "mkdocs-material",
    "mkdocstrings[python]",
    "mkdocstrings-python>=1.16",
    "sqlframe",
    "pandas",
    "polars>=1.0.0",
    "pyarrow",
  ]
  dev = [
    "pre-commit",
    "ruff",
    {include-group = "tests"},
    {include-group = "typing"},
  ]
  dev-core = [
    {include-group = "dev"},
    {include-group = "core"},
  ]
  core-tests = [
    {include-group = "core"},
    {include-group = "tests"},
  ]
  local-dev = [
    {include-group = "dev"},
    {include-group = "core"},
    {include-group = "docs"}
  ]
  
  [tool.hatch.build.targets.sdist]
  include = [
    "narwhals/*",
    "tests/*",
  ]
  
  [tool.hatch.build.targets.wheel]
  include = [
    "narwhals/*",
  ]
  
  [tool.ruff]
  line-length = 90
  fix = true
  target-version = "py39"
  extend-exclude = ["**/this.py"]
  
  [tool.ruff.lint]
  preview = true
  explicit-preview-rules = true
  typing-modules = ["narwhals._typing_compat"]
  
  extend-safe-fixes = [
    "C419",    # unnecessary-comprehension-in-call
    "C420",    # unnecessary-dict-comprehension-for-iterable
    "EM101",   # raw-string-in-exception
    "EM102",   # f-string-in-exception
    "PLR6201", # literal-membership
    "TC",      # flake8-type-checking
    "UP",      # pyupgrade
    "PLW1514", # unspecified-encoding
  ]
  extend-select = [
    "PLR0914", # too-many-locals
    "PLR0916", # too-many-boolean-expressions
    "PLR1702", # too-many-nested-blocks
    "PLR6201", # literal-membership
    "PLW1514", # unspecified-encoding
  ]
  select = ["ALL"]
  ignore = [
    "A001",    # builtin-variable-shadowing
    "A004",    # builtin-import-shadowing
    "ARG002",  # unused-method-argument
    "ANN401",  # any-type
    "COM812",  # missing-trailing-comma
    "D100",    # undocumented-public-module
    "D101",    # undocumented-public-class
    "D102",    # undocumented-public-method
    "D103",    # undocumented-public-function
    "D104",    # undocumented-public-package
    "D105",    # undocumented-magic-method
    "D107",    # undocumented-public-init
    "DTZ001",  # call-datetime-without-tzinfo
    "E501",    # line-too-long
    "FBT003",  # boolean-positional-value-in-call (We enforce at definition site when it is a flag, not a value (e.g. `lit(False)`))
    "FIX",     # flake8-fixme
    "PD010",   # pandas-use-of-dot-pivot-or-unstack
    "PLC0415", # `import` should be at the top-level of a file
    "PLR0913", # too-many-arguments
    "PLR2004", # magic-value-comparison
    "PLW1641", # Object does not implement `__hash__` method
    "SLF001",  # private-member-access
    "TD003",   # missing-todo-link
    "TD004",   # missing-todo-colon
  ]
  
  [tool.ruff.lint.flake8-builtins]
  builtins-ignorelist = ["format"]
  
  [tool.ruff.lint.pep8-naming]
  extend-ignore-names = [
    "Series", 
    "DataFrame", 
    "_Window", 
    "F", 
    "_F",
  ]
  
  [tool.ruff.lint.per-file-ignores]
  "narwhals/expr_dt.py" = ["RUF002"] 
  "narwhals/typing.py" = [
    "PYI042",  # snake-case-type-alias (catches `_1DArray`)
  ] 
  "narwhals/_*/utils.py" = [
    "PLR0911", # too-many-return-statements
  ]
  "tests/*" = [
    "S101", 
    "C901",    # complex-structure
    "PLR0912", # too-many-branches
    "PLR0916", # too-many-boolean-expressions
  ]
  "tpch/tests/*" = ["S101"]
  "utils/*" = ["S311", "T201"]
  "utils/bump_version.py" = ["S603", "S607"]
  "tpch/execute/*" = ["T201"]
  "tpch/notebooks/*" = [
    "ANN001",
    "ANN201",
    "EM101",
    "EXE002",
    "PTH123",
    "T203",
    "TRY003",
  ]
  
  [tool.ruff.lint.pydocstyle]
  convention = "google"
  
  [tool.ruff.lint.pylint]
  max-nested-blocks = 4
  
  [tool.ruff.lint.isort]
  required-imports = ["from __future__ import annotations"]
  combine-as-imports = true
  split-on-trailing-comma = false
  
  [tool.ruff.format]
  docstring-code-format = true
  skip-magic-trailing-comma = true
  
  [tool.pytest.ini_options]
  norecursedirs =  ['*.egg', '.*', '_darcs', 'build', 'CVS', 'dist', 'node_modules', 'venv', '{arch}', 'narwhals/_*']
  testpaths = ["tests"]
  filterwarnings = [
    "error",
    'ignore:.*defaulting to pandas implementation',
    'ignore:.*implementation has mismatches with pandas',
    'ignore:.*You are using pyarrow version',
    # This warning was temporarily raised by pandas but then reverted.
    'ignore:.*Passing a BlockManager to DataFrame:DeprecationWarning',
    # This warning was temporarily raised by Polars but then reverted.
    'ignore:.*The default coalesce behavior of left join will change:DeprecationWarning',
    'ignore: unclosed <socket.socket',
    'ignore:.*The distutils package is deprecated and slated for removal in Python 3.12:DeprecationWarning:pyspark',
    'ignore:.*distutils Version classes are deprecated. Use packaging.version instead.*:DeprecationWarning:pyspark',
    'ignore:.*is_datetime64tz_dtype is deprecated and will be removed in a future version.*:DeprecationWarning:pyspark',
    # Warning raised by PyArrow nightly just by importing pandas
    'ignore:.*Python binding for RankQuantileOptions not exposed:RuntimeWarning:pyarrow',
    'ignore:.*pandas only supports SQLAlchemy:UserWarning:sqlframe',
    'ignore:.*numpy.core is deprecated and has been renamed to numpy._core.*:DeprecationWarning:sqlframe',
    "ignore:.*__array__ implementation doesn't accept a copy keyword, so passing copy=False failed:DeprecationWarning:modin",
    # raised internally by pandas
    "ignore:.*np.find_common_type is deprecated:DeprecationWarning:pandas",
    # Warning raised when calling PandasLikeNamespace.from_arrow with old pyarrow
    "ignore:.*is_sparse is deprecated and will be removed in a future version.*:DeprecationWarning:pyarrow",
    'ignore:.*invalid value encountered in cast:RuntimeWarning:pandas',
  ]
  xfail_strict = true
  markers = ["slow: marks tests as slow (deselect with '-m \"not slow\"')"]
  env = [
    "MODIN_ENGINE=python",
    "PYARROW_IGNORE_TIMEZONE=1",
    "TZ=UTC",
  ]
  
  [tool.coverage.run]
  plugins = ["covdefaults"]
  
  [tool.coverage.report]
  fail_under = 80  # This is just for local development, in CI we set it to 100
  omit = [
    'narwhals/typing.py',
    'narwhals/stable/v1/typing.py',
    'narwhals/stable/v2/typing.py',
    'narwhals/this.py',
    'narwhals/_arrow/typing.py',
    'narwhals/_duckdb/typing.py',
    'narwhals/_spark_like/typing.py',
    'narwhals/_ibis/typing.py',
    # Remove after finishing eager sub-protocols
    'narwhals/_compliant/namespace.py',
  ]
  exclude_also = [
    "if sys.version_info() <",
    "if .*implementation is Implementation.CUDF",
    "if .*implementation is Implementation.MODIN",
    "if .*implementation is Implementation.PYSPARK",
    "if .*implementation is Implementation.PYSPARK_CONNECT",
    "if .*implementation.is_cudf",
    "if .*implementation.is_modin",
    "if .*implementation.is_pyspark",
    "if .*implementation.is_pyspark_connect",
    'request.applymarker\(pytest.mark.xfail',
    'backend_version <',
    '.*._backend_version\(\) <',
    'BACKEND_VERSION <',
    'if ".*" in str\(constructor',
    'pytest.skip\(',
    'assert_never\(',
    'PANDAS_VERSION < \('
  ]
  
  [tool.mypy]
  files = ["narwhals", "tests"]
  pretty = true
  strict = true
  
  [[tool.mypy.overrides]]
  module = [
    "cudf.*",
    "cupy.*",
    "dask.*",
    "dask_expr.*",
    "duckdb.*",
    # https://github.com/ibis-project/ibis/issues/6844
    "ibis.*",
    "joblib.*",
    "modin.*",
    "numpy.*",
    "pyspark.*",
    "sklearn.*",
  ]
  # TODO: remove follow_imports
  follow_imports = "skip"
  ignore_missing_imports = true
  
  [[tool.mypy.overrides]]
  module = [
    "narwhals._expression_parsing",
    "narwhals._utils",
    "narwhals._pandas_like.*",
    "narwhals._ibis.*",
    "narwhals._arrow.*",
    "narwhals._dask.*",
    "narwhals._spark_like.*",
  ]
  warn_return_any = false
  
  [tool.pyright]
  pythonPlatform = "All"
  # NOTE (`pyarrow-stubs` do unsafe `TypeAlias` and `TypeVar` imports)
  # pythonVersion = "3.9"
  reportMissingTypeArgument = "error"
  reportIncompatibleMethodOverride = "error"
  reportMissingImports = "none"
  reportMissingModuleSource = "none"
  reportPrivateImportUsage = "none"
  reportUnusedExpression = "none"    # handled by (https://docs.astral.sh/ruff/rules/unused-variable/)
  typeCheckingMode = "basic"
  include = ["narwhals", "tests"]
  ignore = [
    "../.venv/",
    "../../../**/Lib",      # stdlib
    "../../../**/typeshed*" # typeshed-fallback
  ]
  