content: [project]
  name = "singer-sdk"
  dynamic = [
      "version",
  ]
  description = "A framework for building Singer taps and targets"
  authors = [{ name = "Meltano Team and Contributors", email = "hello@meltano.com" }]
  maintainers = [{ name = "Meltano Team and Contributors", email = "hello@meltano.com" }]
  keywords = [
      "Meltano",
      "Singer",
      "Meltano SDK",
      "Singer SDK",
      "ELT",
  ]
  classifiers = [
      "Intended Audience :: Developers",
      "Operating System :: OS Independent",
      "Programming Language :: Python :: 3.10",
      "Programming Language :: Python :: 3.11",
      "Programming Language :: Python :: 3.12",
      "Programming Language :: Python :: 3.13",
      "Programming Language :: Python :: 3.14",
      "Programming Language :: Python :: Implementation :: CPython",
      "Topic :: Software Development :: Libraries :: Application Frameworks",
      "Typing :: Typed",
  ]
  readme = "README.md"
  license = "Apache-2.0"
  license-files = ["LICENSE"]
  requires-python = ">=3.10"
  
  dependencies = [
      "backoff>=2.0.0,<4",
      'backports-datetime-fromisoformat>=2.0.1; python_version<"3.11"',
      "click>=8.2,<9",
      "fsspec>=2024.9.0",
      'importlib-metadata>=6.5; python_version<"3.12"',
      "inflection>=0.5.1",
      "joblib>=1.3.0",
      "jsonpath-ng>=1.5.3",
      "jsonschema>=4.16.0",
      "packaging>=23.1",
      "python-dotenv>=0.20",
      "PyYAML>=6.0",
      "referencing>=0.30.0",
      "requests>=2.25.1",
      "simpleeval>=0.9.13,!=1.0.1",
      "simplejson>=3.17.6",
      "sqlalchemy>=2",
      "typing-extensions>=4.5.0; python_version < '3.13'",
      "universal-pathlib>=0.2.6",
  ]
  
  [project.optional-dependencies]
  s3 = [
      "s3fs>=2024.9.0",
  ]
  
  parquet = [
      "pyarrow>=15",
  ]
  
  testing = [
      "pytest>=7.5",
  ]
  
  faker = [
      "faker>=22.5",
  ]
  
  jwt = [
      "cryptography>=3.4.6",
      "PyJWT>=2.4.0",
  ]
  
  ssh = [
      "paramiko>=3.3.0",
  ]
  
  msgspec = [
      "msgspec>=0.19.0",
  ]
  
  sql = [
      "sqlalchemy>=2",
  ]
  
  [project.urls]
  Homepage = "https://sdk.meltano.com/en/latest/"
  Repository = "https://github.com/meltano/sdk"
  Documentation = "https://sdk.meltano.com/en/latest/"
  "Issue Tracker" = "https://github.com/meltano/sdk/issues"
  "Twitter" = "https://twitter.com/meltanodata/"
  "Changelog" = "https://github.com/meltano/sdk/blob/main/CHANGELOG.md"
  "Slack" = "https://meltano.com/slack"
  "Youtube" = "https://www.youtube.com/meltano"
  
  [dependency-groups]
  dev = [
      {"include-group" = "docs"},
      {"include-group" = "testing"},
      {"include-group" = "typing"},
      "deptry>=0.15.0",
  ]
  docs = [
      "furo>=2024.5.6",
      "myst-parser>=3",
      "pytest>=8",
      "sphinx>=7",
      "sphinx-copybutton>=0.5.2",
      "sphinx-inline-tabs>=2023.4.21",
      "sphinx-notfound-page>=1.0.0",
      "sphinx-reredirects>=0.1.5",
  ]
  # Workspace packages needed for end-to-end tests
  packages = [
      "mapper-custom",
      "tap-countries",
      "tap-csv",
      "tap-dummyjson",
      "tap-fake-people",
      "tap-fundamentals",
      "tap-gitlab",
      "tap-hostile",
      "tap-sqlite",
      "target-csv",
      "target-parquet",
      "target-sqlite",
  ]
  testing = [
      "coverage[toml]>=7.9",
      "fastjsonschema>=2.19.1",
      "pytest>=7.2.1",
      "pytest-benchmark>=4.0.0",
      "pytest-github-actions-annotate-failures>=0.3.0",
      "pytest-snapshot>=0.9.0",
      "pytest-subtests>=0.13.1",
      "pytz>=2022.2.1",
      "requests-cache>=1.2.1",
      "requests-mock>=1.10.0",
      "rfc3339-validator>=0.1.4",
      "time-machine>=2.10.0",
      "toolz>=1.0.0",
      "xdoctest[colors]>=1.1.1",
  ]
  typing = [
      "mypy>=1.17",
      "pyarrow-stubs>=19.1",
      "types-jsonschema>=4.17.0.6",
      "types-pytz>=2022.7.1.2",
      "types-requests>=2.28.11",
      "types-simplejson>=3.18.0",
      "types-PyYAML>=6.0.12",
  ]
  benchmark = [
      {"include-group" = "testing"},
      "pytest-codspeed>=2.2.0,!=4.1.0",
  ]
  
  [tool.hatch.build.targets.sdist]
  include = [
      "cookiecutter",
      "docs",
      "fixtures",
      "packages",
      "samples",
      "singer_sdk",
      "tests",
  ]
  
  [tool.hatch.build.targets.wheel]
  include = [
      "singer_sdk",
  ]
  
  [tool.hatch.version]
  fallback-version = "0.0.0"
  source = "vcs"
  
  [tool.pytest.ini_options]
  addopts = [
      "--durations=10",
      "-m",
      "not contrib and not external and not packages",
      "-ra",
      "--strict-config",
      "--strict-markers",
  ]
  filterwarnings = [
      "error",
      "once:'asyncio.iscoroutinefunction' is deprecated:DeprecationWarning",  # https://github.com/litl/backoff/pull/220
      "once:No records were available to test:UserWarning",
      # https://github.com/meltano/sdk/issues/1354
      "ignore:The function singer_sdk.testing.get_standard_tap_tests is deprecated:DeprecationWarning",
      # TODO: Address this SQLite warning in Python 3.13+
      "ignore::ResourceWarning",
      # Our own warnings
      "once::singer_sdk.helpers._compat.SingerSDKDeprecationWarning",
  ]
  log_cli_level = "INFO"
  log_format = "%(levelname)s %(name)s %(message)s"
  markers = [
      "contrib: Tests for contrib (experimental or optional) modules",
      "external: Tests relying on external resources",
      "packages: Tests for sample taps and targets",
      "windows: Tests that only run on Windows",
      "linux: Tests that only run on Linux",
      "darwin: Tests that only run on Darwin",
      "snapshot: Tests that use pytest-snapshot",
  ]
  minversion = "7"
  testpaths = ["tests"]
  norecursedirs = "cookiecutter"
  xfail_strict = false
  
  [tool.commitizen]
  name = "cz_version_bump"
  version = "0.50.2"
  changelog_merge_prerelease = true
  prerelease_offset = 1
  tag_format = "v$major.$minor.$patch$prerelease"
  version_files = [
      "docs/conf.py:^release =",
      """cookiecutter/tap-template/{{cookiecutter.tap_id}}/pyproject.toml:^    "singer-sdk""",
      """cookiecutter/target-template/{{cookiecutter.target_id}}/pyproject.toml:^    "singer-sdk""",
      """cookiecutter/mapper-template/{{cookiecutter.mapper_id}}/pyproject.toml:^    "singer-sdk""",
      ".github/ISSUE_TEMPLATE/bug.yml:^      placeholder:",
  ]
  
  [tool.coverage.paths]
  source = [
      "singer_sdk/",
      "*/singer_sdk",
  ]
  tests = [
      "tests/",
      "*/tests",
  ]
  
  [tool.coverage.run]
  branch = true
  core = "sysmon"
  source = ["singer_sdk", "tests"]
  relative_files = true  # This allows coverage to be measured in Windows
  omit = [
      "tests/*",
      "samples/*",
      "packages/*",
      "singer_sdk/helpers/_compat.py",
      "singer_sdk/helpers/types.py",
  ]
  
  [tool.coverage.report]
  exclude_also = [
      "def __repr__",
      "raise AssertionError",
      "raise NotImplementedError",
      "if __name__ == .__main__.:",
      '''class .*\bProtocol\):''',
      '''@(abc\.)?abstractmethod''',
      '''if (t\.)?TYPE_CHECKING:''',
      "assert_never",
  ]
  show_missing = true
  
  [tool.deptry]
  known_first_party = ["singer_sdk"]
  pep621_dev_dependency_groups = ["docs", "testing"]
  
  [tool.deptry.package_module_name_map]
  backports-datetime-fromisoformat = "backports"
  importlib-metadata = "importlib_metadata"
  importlib-resources = "importlib_resources"
  PyJWT = "jwt"
  
  [tool.deptry.per_rule_ignores]
  DEP002 = [
      # Plugins
      "paramiko",
      "s3fs",
      # Legacy dependencies
      "importlib-resources",
  ]
  DEP004 = [
      # TODO: Make pytest a runtime dependency?
      "pytest",
  ]
  
  [tool.mypy]
  enable_error_code = [
      "exhaustive-match",
      "ignore-without-code",
      "redundant-expr",
      "truthy-bool",
  ]
  files = "singer_sdk"
  local_partial_types = true
  strict = false
  warn_redundant_casts = true
  warn_return_any = true
  warn_unreachable = true
  warn_unused_configs = true
  warn_unused_ignores = true
  
  [[tool.mypy.overrides]]
  ignore_missing_imports = true
  module = [
      "backports.datetime_fromisoformat.*",
      "joblib.*",       # https://github.com/joblib/joblib/issues/1516
      "fsspec.*",       # https://github.com/fsspec/filesystem_spec/issues/625
      "jsonpath_ng.*",  # https://github.com/h2non/jsonpath-ng/issues/152
      "pyarrow.*",      # https://github.com/apache/arrow/issues/32609
      "toolz.*",        # https://github.com/pytoolz/toolz/issues/496
  ]
  
  [build-system]
  requires = [
      "hatchling==1.27",
      "hatch-vcs==0.5",
  ]
  build-backend = "hatchling.build"
  
  [project.entry-points."pytest11"]
  singer_testing = "singer_sdk.testing.pytest_plugin"
  
  [project.entry-points."singer_sdk.batch_encoders"]
  jsonl = "singer_sdk.contrib.batch_encoder_jsonl:JSONLinesBatcher"
  parquet = "singer_sdk.contrib.batch_encoder_parquet:ParquetBatcher"
  
  [tool.ruff]
  extend-exclude = [
      "cookiecutter/*",
  ]
  line-length = 88
  required-version = ">=0.13"
  
  [tool.ruff.format]
  docstring-code-format = true
  
  [tool.ruff.lint]
  explicit-preview-rules = false
  future-annotations = true
  ignore = [
      "N818",    # Exception name should be named with an Error suffix
      "COM812",  # missing-trailing-comma
  ]
  preview = true
  select = [
      "F",    # Pyflakes
      "E",    # pycodestyle (error)
      "W",    # pycodestyle (warning)
      "C90",  # mccabe
      "DOC",  # pydocstyle
      "I",    # isort
      "N",    # pep8-naming
      "D",    # pydocstyle/flake8-docstrings
      "UP",   # pyupgrade
      "YTT",  # flake8-2020
      "ANN",  # flake8-annotations
      "S",    # flake8-bandit
      "BLE",  # flake8-blind-except
      "FBT",  # flake8-boolean-trap
      "B",    # flake8-bugbear
      "A",    # flake8-builtins
      "COM",  # flake8-commas
      "C4",   # flake8-comprehensions
      "DTZ",  # flake8-datetimez
      "T10",  # flake8-debugger
      "EM",   # flake8-errmsg
      "FA",   # flake8-future-annotations
      "ISC",  # flake8-implicit-str-concat
      "ICN",  # flake8-import-conventions
      "G",    # flake8-logging-format
      "INP",  # flake8-no-pep420
      "PIE",  # flake8-pie
      "T20",  # flake8-print
      "PYI",  # flake8-pyi
      "PT",   # flake8-pytest-style
      "Q",    # flake8-quotes
      "RSE",  # flake8-raise
      "RET",  # flake8-return
      "SLF",  # flake8-self
      "SIM",  # flake8-simplify
      "TID",  # flake8-tidy-imports
      "TC",   # flake8-type-checking
      "ARG",  # flake8-unused-arguments
      "PTH",  # flake8-use-pathlib
      "ERA",  # eradicate
      "PGH",  # pygrep-hooks
      "PLC",  # pylint (convention)
      "PLE",  # pylint (error)
      "PLR",  # pylint (refactor)
      "PLW",  # pylint (warning)
      "TRY",  # tryceratops
      "FLY",  # flynt
      "PERF", # perflint
      "RUF",  # ruff
  ]
  unfixable = [
      "ERA",  # Don't remove commented out code
  ]
  
  [tool.ruff.lint.per-file-ignores]
  "docs/conf.py" = [
      "D",      # pydocstyle/flake8-docstrings
      "I002",   # isort: missing-required-import
      "INP001", # flake8-no-pep420: implicit-namespace-package
  ]
  "noxfile.py" = ["ANN"]
  "tests/*" = [
      "ANN",
      "D1",
      "D2",
      "DOC",
      "FBT001",
      "FBT003",
      "INP",      # Allow implicit namespace packages in tests
      "PLR2004",
      "S101",
      "SLF001",   # Allow private method access in tests
      "PLC2701",  # Allow usage of private members in tests
      "PLR6301",  # Don't suggest making test methods static, etc.
  ]
  # Disabled some checks in helper modules
  "singer_sdk/helpers/_*.py" = ["DOC"]
  # Disabled some checks in samples code
  "samples/*" = ["ANN", "DOC"]
  "packages/*" = ["DOC"]
  # Templates support a generic resource of type Any.
  "singer_sdk/testing/*.py" = ["S101"]
  "singer_sdk/testing/templates.py" = ["ANN401"]
  
  [tool.ruff.lint.flake8-annotations]
  allow-star-arg-any = true
  mypy-init-return = true
  suppress-dummy-args = true
  
  [tool.ruff.lint.flake8-import-conventions]
  banned-from = ["typing"]
  
  [tool.ruff.lint.flake8-import-conventions.extend-aliases]
  typing = "t"
  
  [tool.ruff.lint.flake8-pytest-style]
  fixture-parentheses = false
  parametrize-names-type = "csv"
  
  [tool.ruff.lint.isort]
  known-first-party = [
      "singer_sdk",
      "samples",
      "tests",
  ]
  required-imports = ["from __future__ import annotations"]
  
  [tool.ruff.lint.pep8-naming]
  classmethod-decorators = [
      "singer_sdk.cli.plugin_cli",
  ]
  
  [tool.ruff.lint.pydocstyle]
  convention = "google"
  
  [tool.ruff.lint.pylint]
  max-args = 9
  
  [tool.uv]
  required-version = ">=0.5.19"
  
  [tool.uv.sources]
  mapper-custom = { workspace = true }
  tap-countries = { workspace = true }
  tap-csv = { workspace = true }
  tap-dummyjson = { workspace = true }
  tap-fake-people = { workspace = true }
  tap-fundamentals = { workspace = true }
  tap-gitlab = { workspace = true }
  tap-hostile = { workspace = true }
  tap-sqlite = { workspace = true }
  target-csv = { workspace = true }
  target-parquet = { workspace = true }
  target-sqlite = { workspace = true }
  
  [tool.uv.workspace]
  members = [
      "packages/*",
  ]
  
  [tool.codespell]
  skip = "*.csv,samples/aapl/*.json,packages/*/schemas/*.json,samples/*/schemas/*.json,.http_cache/*.json,tests/*/snapshots/*/*.jsonl"
  ignore-words-list = "fo,intoto"
  