content: [tool.poetry]
  name = "nautobot"
  # Primary package version gets set here. This is used for publishing, and once
  # installed, `nautobot.__version__` will have this version number.
  version = "2.4.20b1"
  description = "Source of truth and network automation platform."
  authors = ["Network to Code <opensource@networktocode.com>"]
  license = "Apache-2.0"
  homepage = "https://nautobot.com"
  repository = "https://github.com/nautobot/nautobot"
  documentation = "https://docs.nautobot.com"
  readme = "README.md"
  keywords = ["Nautobot"]
  classifiers = [
      "Intended Audience :: Developers",
      "Development Status :: 5 - Production/Stable",
      "Programming Language :: Python :: 3",
      "Programming Language :: Python :: 3.10",
      "Programming Language :: Python :: 3.11",
      "Programming Language :: Python :: 3.12",
  ]
  packages = [
      {include = "nautobot"}
  ]
  include = [
      # Rendered documentation - Poetry by default would exclude these files as they are in .gitignore
      "nautobot/project-static/docs/**/*",
  ]
  exclude = [
      # Source code of the documentation doesn't need to be included since we package the rendered docs
      "nautobot/docs/**/*",
  ]
  
  [tool.poetry.dependencies]
  python = ">=3.10,<3.13"
  # Background task processing
  celery = "~5.5.3"
  # Cryptographic recipes and primitives.
  # Not directly used by Nautobot itself, but key to many of Nautobot's dependencies.
  # Added as an explicit dependency to protect against various CVEs in older versions.
  cryptography = "~45.0.7"
  # Fundamental web framework for Nautobot
  Django = "~4.2.25"
  # Adds AJAX capabilities to django-tables2
  django-ajax-tables = "~1.1.1"
  # LDAP Support
  django-auth-ldap = {version = "~5.2.0", optional = true}
  # Base functionality for task scheduling with Celery
  # Not yet upgrading to v2.8.x due to numerous reported issues, including:
  # https://github.com/celery/django-celery-beat/issues/894
  # https://github.com/celery/django-celery-beat/issues/922
  # https://github.com/celery/django-celery-beat/issues/927
  django-celery-beat = "~2.7.0"
  # Provides a database backend & models for Celery task results
  django-celery-results = "~2.6.0"
  # Management of app configuration via the Django admin UI
  django-constance = "~4.3.2"
  # Permit cross-domain API requests
  django-cors-headers = "~4.7.0"
  # Store files in the database for background tasks
  django-db-file-storage = "~0.5.6.1"
  # AutoSlugField and shell_plus
  django-extensions = "~4.1"
  # Advanced query filters
  django-filter = "~25.1"
  # Health check endpoint
  django-health-check = "~3.20.0"
  # Django Jinja - jinja2 backend for django, used to easily register custom jinja filters and render jinja.
  django-jinja = "~2.11.0"
  # Prometheus metrics for Django
  django-prometheus = "~2.4.1"
  # Redis cache for Django used for distributed locking
  django-redis = "~6.0.0"
  # Django silk for request profiling
  django-silk = "~5.4.3"
  # External Storage support (i.e. S3)
  django-storages = {version = "~1.14.6", optional = true}
  # Structured logging for Django
  django-structlog = {version = "^9.1.1", extras = ["celery"]}
  # Advanced HTML tables
  django-tables2 = "~2.7.5"
  # Tags
  django-taggit = "~6.1.0"
  # Represent time zones in Django
  django-timezone-field = "~7.1"
  # Tree database structures based on Common Table Expressions
  django-tree-queries = "~0.20.0"
  # Run production webservers such as uWSGI/gunicorn as a Django management command.
  django-webserver = "~1.2.0"
  # REST API framework
  djangorestframework = "~3.16.1"
  # OpenAPI 3.0 schema generation for the REST API
  drf-spectacular = {version = "~0.28.0", extras = ["sidecar"]}
  # Emoji terminal output for Python.
  emoji = "~2.14.1"
  # Git integrations for Python
  GitPython = "~3.1.45"
  # GraphQL support
  # NOTE: graphene-django 3.x is available but includes breaking changes. Will address in a future release.
  graphene-django = "~2.16.0"
  # Graphene Optimizer
  # NOTE: graphene-django-optimizer 0.9 and later require Graphene v3
  graphene-django-optimizer = "~0.8.0"
  # Template rendering engine
  Jinja2 = "~3.1.6"
  # Optional data validation of config contexts - loose dependency since our usage is light but apps may be more specific
  jsonschema = "^4.7.0"
  # Library to access Kubernetes API used to develop K8s integration
  kubernetes = "^33.1.0"
  # Rendering of markdown files to HTML
  Markdown = "~3.8.2"
  # MySQL database adapter
  mysqlclient = {version = "~2.2.7", optional = true}
  # NAPALM automation library
  napalm = {version = ">=4.1.0,<6.0.0", optional = true}
  # IP prefix and address handling
  netaddr = "~1.3.0"
  # Library of network related utilities including network driver mappings and Jinja Filters
  # Note: netutils is limited in scope, dependencies, and observes semver, as such
  #       we permit a looser (^) version constraint here.
  netutils = "^1.14.0"
  # HTML sanitization
  nh3 = "~0.3.1"
  # Handling of version numbers
  packaging = ">=23.1"
  # Image processing library
  Pillow = "^11.0"
  # Custom prometheus metrics
  prometheus-client = "~0.22.1"
  # PostgreSQL database adapter
  # NOTE: psycopg3 is available now and nominally replaces psycopg2
  psycopg2-binary = "~2.9.11"
  # A Python slugify application that handles unicode.
  python-slugify = "~8.0.4"
  # The uWSGI WSGI HTTP server as a Python module
  pyuwsgi = "~2.0.30"
  # YAML parsing and rendering
  PyYAML = "~6.0.3"
  # Social authentication core
  # Note that social-auth-app-django (below) is *not* an optional dependency, and it requires social-auth-core in turn.
  # The only part of this dependency that is actually optional is in fact social-auth-core's "saml" extra.
  social-auth-core = {version = "~4.7.0", optional = true, extras = ["saml"]}
  # Social authentication/registration with support for many auth providers
  social-auth-app-django = "~5.4.3"
  # Rendering of SVG images (for rack elevations, etc.)
  svgwrite = "~1.4.3"
  
  [tool.poetry.extras]
  all = ["django-auth-ldap", "django-storages", "mysqlclient", "napalm", "social-auth-core"]
  ldap = ["django-auth-ldap"]
  mysql = ["mysqlclient"]
  napalm = ["napalm"]
  # This is not currently possible, there is a feature request with Poetry https://github.com/python-poetry/poetry/issues/3913
  # openid = ["social-auth-core[openidconnect]"]
  remote_storage = ["django-storages"]
  # This is not currently possible, there is a feature request with Poetry https://github.com/python-poetry/poetry/issues/3913
  # saml = ["social-auth-core[saml]"]
  sso = ["social-auth-core"]
  
  [tool.poetry.group.dev.dependencies]
  # Tool for debugging Django
  # Currently pinned to 5.2.0 due to https://github.com/django-commons/django-debug-toolbar/issues/2168
  django-debug-toolbar = "~5.2.0"
  # Nautobot example App used for testing
  example-app = {path = "examples/example_app", develop = true}
  # Nautobot test App used for testing core view overrides
  example-app-with-view-override = {path = "examples/example_app_with_view_override", develop = true}
  # Random data generation
  factory-boy = "~3.3.3"
  # Factory Boy dependency.
  faker = "^37.6.0"
  # Alternative to Make, CLI based on `tasks.py`
  invoke = "~2.2.0"
  # Colorization and autoformatting of CLI output, used for Invoke tasks
  rich = "~14.1.0"
  # Change log management and generation
  towncrier = "~24.8.0"
  # Watch files for celery and mkdocs reload
  watchdog = "~6.0.0"
  
  [tool.poetry.group.docs.dependencies]
  # Rendering docs to HTML
  mkdocs = "~1.6.1"
  # Embedding YAML files into Markdown documents as tables
  markdown-data-tables = "~1.0.0"
  # Render custom markdown for version added/changed/remove notes
  markdown-version-annotations = "~1.0.1"
  # Automatically generate some files as part of mkdocs build
  mkdocs-gen-files = "~0.5.0"
  # Image lightboxing in mkdocs
  mkdocs-glightbox = "^0.4.0"
  # Use Jinja2 templating in docs - see settings.md
  mkdocs-macros-plugin = "~1.3.9"
  # Material for mkdocs theme
  mkdocs-material = "~9.6.21"
  # Handle docs redirections
  mkdocs-redirects = "~1.2.2"
  # Automatically handle index pages for docs sections
  mkdocs-section-index = "~0.3.10"
  # Automatic documentation from sources, for MkDocs
  mkdocstrings = "~0.30.1"
  # Python-specific extension to mkdocstrings
  mkdocstrings-python = "~1.16.12"
  
  [tool.poetry.group.linting.dependencies]
  # Django template files indenter
  djhtml = "~3.0.10"
  # Django template files linter and formatter
  djlint = "~1.36.4"
  # Code static analysis
  pylint = "~3.3.9"
  # Pylint extensions for Django
  pylint-django = "~2.6.1"
  # Python implementation of markdownlint
  pymarkdownlnt = "~0.9.32"
  # Combination linter and code formatter
  ruff = "~0.12.12"
  # YAML linting
  yamllint = "~1.37.1"
  
  [tool.poetry.group.testing.dependencies]
  # Test code coverage measurement - pinned to 6.4.0 to resolve https://github.com/nedbat/coveragepy/issues/1420
  coverage = "<=6.4.0"
  # Testing of data migrations
  django-test-migrations = "~1.4.0"
  # OpenAPI validator to test that our generated schema is valid
  openapi-spec-validator = "~0.7.2"
  # Integration Tests
  requests = "~2.32.5"
  # Selenium web drivers for live integration testing
  selenium = "~4.34.2"
  # Abstraction layer for working with Selenium
  splinter = "~0.21.0"
  # Serialization of tracebacks - used when running unittest with --parallel flag
  tblib = "~3.1.0"
  # Time travel for testing, used in ScheduledJob tests etc.
  time-machine = "~2.17.0"
  
  [tool.poetry.scripts]
  nautobot-server = "nautobot.core.cli:main"
  
  [build-system]
  requires = ["poetry-core>=1.0.0"]
  build-backend = "poetry.core.masonry.api"
  
  [tool.coverage.run]
  concurrency = ["multiprocessing", "thread"]
  disable_warnings = ["already-imported"]
  include = [
      "nautobot/*",
  ]
  omit = [
      # Don't measure coverage of test code
      "nautobot/**/tests/**/*.py",
      "nautobot/core/testing/*.py",
      "nautobot/extras/test_jobs/*.py",
  ]
  relative_files = true
  
  [tool.djlint]
  ignore = "H005,H006,H008,H011,H012,H013,H014,H015,H016,H019,H020,H021,H022,H023,H025,H026,H030,H031,H037,T001,T002,T003"
  profile="django"
  extend_exclude = "docs/,example_app/static,nautobot/project-static/"
  
  [tool.pylint.master]
  load-plugins = "pylint_django"
  py-version = "3.10"
  ignore-paths = [
      ".*third_party.*",
      "nautobot/extras/tests/git_data/02-invalid-files/",
  ]
  jobs = 0
  
  [tool.pylint.basic]
  # No docstrings required yet for private functions, or for test_* functions, or for inner Meta classes.
  no-docstring-rgx="^(_|test_|Meta$)"
  # Don't complain about "pk" as a variable name
  good-names = """_,i,j,k,pk"""
  
  [tool.pylint.message_control]
  # TODO: re-enable and fix these as time permits
  disable = [
      # Warnings
      "abstract-method",
      "attribute-defined-outside-init",
      "broad-except",
      "fixme",
      "global-statement",
      "global-variable-not-assigned",
      "logging-format-interpolation",
      "logging-fstring-interpolation",
      "pointless-statement",
      "pointless-string-statement",
      "protected-access",
      "raise-missing-from",
      "self-assigning-variable",
      "signature-differs",
      "unspecified-encoding",
      "unused-argument",
      "unused-import",  # covered by ruff?
      "unused-wildcard-import",
      "wildcard-import",
      # Conventions
      "consider-iterating-dictionary",
      "empty-docstring",
      "import-outside-toplevel",
      "invalid-name",
      "keyword-arg-before-vararg",  # https://github.com/pylint-dev/pylint/issues/7396
      "line-too-long",
      "missing-class-docstring",
      "missing-function-docstring",
      "missing-module-docstring",
      "superfluous-parens",
      "too-many-lines",
      "ungrouped-imports",
      "unnecessary-dunder-call",
      "use-maxsplit-arg",
      "wrong-import-order",
      "wrong-import-position",
      # Refactoring
      "consider-using-from-import",
      "consider-using-in",
      "consider-using-generator",
      "cyclic-import",
      "duplicate-code",
      "no-else-break",
      "no-else-raise",
      "no-else-return",
      "super-with-arguments",
      "too-few-public-methods",
      "too-many-ancestors",
      "too-many-arguments",
      "too-many-boolean-expressions",
      "too-many-branches",
      "too-many-instance-attributes",
      "too-many-locals",
      "too-many-nested-blocks",
      "too-many-positional-arguments",
      "too-many-public-methods",
      "too-many-return-statements",
      "too-many-statements",
      # Pylint-Django
      "modelform-uses-exclude",
  ]
  
  [tool.pylint.miscellaneous]
  # We don't want to fail on "TODO" comments as there are plenty of those in our code for good reason
  notes = ["FIXME", "XXX"]
  
  [tool.pylint.typecheck]
  # Regex patterns describing dynamically defined attributes that cause false reports for "no-member" checker
  generated-members = [
      ".*Factory\\..*",
      ".*FactoryOptions\\..*",
      ".*FilterSet\\._meta",
      ".*FilterSet\\.base_filters",
      ".*FilterSet\\.declared_filters",
      ".*Job\\.request",
      ".*LazyAttribute\\..*",  # factory attributes
      ".*Maybe\\..*",  # factory attributes
      ".*Serializer\\.Meta",
      ".*Table\\._meta",
      ".*Table\\.base_columns",
      ".*Table\\.context",
  ]
  extension-pkg-allow-list = [
      "nh3",
  ]
  # @patch changes the signature of a function it's applied to; don't raise "no-value-for-parameter" here
  signature-mutators = ["unittest.mock.patch"]
  
  [tool.pymarkdown]
  extensions.front-matter.enabled = true  # needed for mkdocs-macros compatibility
  plugins.additional_paths = "scripts/file_relative_links.py"
  plugins.code-block-style.enabled = false  # inconsistency between "code" blocks and "admonition" blocks is expected
  plugins.line-length.enabled = false  # we don't wrap markdown to 80 characters.
  plugins.list-anchored-indent.enabled = true  # in place of ul-indent, see below
  plugins.proper-names.names = "Celery,Django,Nautobot,Pylint,PyMarkdown,Python,REST API"
  plugins.proper-names.code_blocks = false  # don't check proper nouns inside ```code blocks```
  plugins.proper-names.code_spans = false  # don't check proper nouns inside `code spans`
  plugins.ul-indent.enabled = false  # doesn't work well with MkDocs expectations, use list-anchored-indent instead
  
  [tool.ruff]
  line-length = 120
  target-version = "py310"
  extend-exclude = [
      # Intentionally malformed files
      "nautobot/extras/tests/git_data/02-invalid-files/jobs",
  ]
  
  [tool.ruff.lint]
  select = [
      "DJ",  # flake8-django
      "E",  # pycodestyle
      "F",  # pyflakes
      "I",  # isort
      "PIE",  # flake8-pie
      "PLE",  # pylint errors
      "RUF",  # ruff itself
      "S",  # flake8-bandit
      "W",  # pycodestyle
  ]
  ignore = [
      "E501",  # pycodestyle: line-too-long
      "PIE808",  # unnecessary-range-start
      "RUF012",  # mutable-class-default - see https://github.com/astral-sh/ruff/issues/5243
  ]
  
  [tool.ruff.lint.isort]
  combine-as-imports = true
  force-sort-within-sections = true
  known-local-folder = ["example_app"]
  order-by-type = false
  
  [tool.towncrier]
  package = "nautobot"
  directory = "changes"
  filename = "nautobot/docs/release-notes/version-2.4.md"
  template = "development/towncrier_template.j2"
  start_string = "<!-- towncrier release notes start -->"
  issue_format = "[#{issue}](https://github.com/nautobot/nautobot/issues/{issue})"
  
  [[tool.towncrier.type]]
  directory = "security"
  name = "Security"
  showcontent = true
  
  [[tool.towncrier.type]]
  directory = "added"
  name = "Added"
  showcontent = true
  
  [[tool.towncrier.type]]
  directory = "changed"
  name = "Changed"
  showcontent = true
  
  [[tool.towncrier.type]]
  directory = "deprecated"
  name = "Deprecated"
  showcontent = true
  
  [[tool.towncrier.type]]
  directory = "removed"
  name = "Removed"
  showcontent = true
  
  [[tool.towncrier.type]]
  directory = "fixed"
  name = "Fixed"
  showcontent = true
  
  [[tool.towncrier.type]]
  directory = "dependencies"
  name = "Dependencies"
  showcontent = true
  
  [[tool.towncrier.type]]
  directory = "documentation"
  name = "Documentation"
  showcontent = true
  
  [[tool.towncrier.type]]
  directory = "housekeeping"
  name = "Housekeeping"
  showcontent = true
  