diff --git a/.bandit.yml b/.bandit.yml deleted file mode 100644 index 4824c564..00000000 --- a/.bandit.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -# No need to check for security issues in the test scripts! -exclude_dirs: - - "./nautobot_chatops/tests/" - -skips: - - "B404" diff --git a/.flake8 b/.flake8 deleted file mode 100644 index 8193a5d0..00000000 --- a/.flake8 +++ /dev/null @@ -1,4 +0,0 @@ -[flake8] -# E501: Line length is enforced by Black, so flake8 doesn't need to check it -# W503: Black disagrees with this rule, as does PEP 8; Black wins -ignore = E501, W503, F811, F401 \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d8658bb3..25039125 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,7 +8,7 @@ on: # yamllint disable - "pull_request" jobs: - black: + ruff-format: runs-on: "ubuntu-20.04" env: INVOKE_NAUTOBOT_CHATOPS_LOCAL: "True" @@ -19,9 +19,9 @@ jobs: uses: "networktocode/gh-action-setup-poetry-environment@v2" with: python-version: "3.10" - - name: "Linting: black" - run: "poetry run invoke black" - bandit: + - name: "Linting: ruff format" + run: "poetry run invoke ruff --action format" + ruff-lint: runs-on: "ubuntu-20.04" env: INVOKE_NAUTOBOT_CHATOPS_LOCAL: "True" @@ -32,40 +32,8 @@ jobs: uses: "networktocode/gh-action-setup-poetry-environment@v2" with: python-version: "3.10" - - name: "Linting: bandit" - run: "poetry run invoke bandit" - needs: - - "black" - pydocstyle: - runs-on: "ubuntu-20.04" - env: - INVOKE_NAUTOBOT_CHATOPS_LOCAL: "True" - steps: - - name: "Check out repository code" - uses: "actions/checkout@v2" - - name: "Setup environment" - uses: "networktocode/gh-action-setup-poetry-environment@v2" - with: - python-version: "3.10" - - name: "Linting: pydocstyle" - run: "poetry run invoke pydocstyle" - needs: - - "black" - flake8: - runs-on: "ubuntu-20.04" - env: - INVOKE_NAUTOBOT_CHATOPS_LOCAL: "True" - steps: - - name: "Check out repository code" - uses: "actions/checkout@v2" - - name: "Setup environment" - uses: "networktocode/gh-action-setup-poetry-environment@v2" - with: - python-version: "3.10" - - name: "Linting: flake8" - run: "poetry run invoke flake8" - needs: - - "black" + - name: "Linting: ruff" + run: "poetry run invoke ruff" yamllint: runs-on: "ubuntu-20.04" env: @@ -79,8 +47,6 @@ jobs: python-version: "3.10" - name: "Linting: yamllint" run: "poetry run invoke yamllint" - needs: - - "black" pylint: runs-on: "ubuntu-20.04" steps: @@ -95,16 +61,14 @@ jobs: - name: "Linting: Pylint" run: "poetry run invoke pylint" needs: - - "bandit" - - "pydocstyle" - - "flake8" + - "ruff-lint" - "yamllint" unittest: strategy: fail-fast: true matrix: python-version: ["3.8", "3.9", "3.10"] - nautobot-version: ["1.6.2", "stable"] + nautobot-version: ["1.6.2", "1.6"] runs-on: "ubuntu-20.04" env: INVOKE_NAUTOBOT_CHATOPS_PYTHON_VER: "${{ matrix.python-version }}" diff --git a/changes/323.housekeeping b/changes/323.housekeeping new file mode 100644 index 00000000..74b24e11 --- /dev/null +++ b/changes/323.housekeeping @@ -0,0 +1 @@ +Pinning pycodestyle 2.7.0 to resolve issue with Upstream Testing. \ No newline at end of file diff --git a/development/mattermost/nautobot_bootstrap.py b/development/mattermost/nautobot_bootstrap.py index 41d6057a..ad4974e8 100644 --- a/development/mattermost/nautobot_bootstrap.py +++ b/development/mattermost/nautobot_bootstrap.py @@ -1,6 +1,6 @@ """Bootstrap script for Nautobot to allow Mattermost integration.""" -from nautobot_chatops.models import AccessGrantTypeChoices, CommandTokenPlatformChoices, AccessGrant, CommandToken +from nautobot_chatops.models import AccessGrant, AccessGrantTypeChoices, CommandToken, CommandTokenPlatformChoices for grant_type in AccessGrantTypeChoices.values(): AccessGrant.objects.update_or_create( diff --git a/development/nautobot_config.py b/development/nautobot_config.py index a8613b93..0b3f2416 100644 --- a/development/nautobot_config.py +++ b/development/nautobot_config.py @@ -1,4 +1,5 @@ """Nautobot development configuration file.""" + import os import sys diff --git a/nautobot_chatops/__init__.py b/nautobot_chatops/__init__.py index 97ded220..987af17b 100644 --- a/nautobot_chatops/__init__.py +++ b/nautobot_chatops/__init__.py @@ -1,4 +1,5 @@ """Nautobot plugin implementing a chatbot.""" + try: from importlib import metadata except ImportError: @@ -10,7 +11,6 @@ from django.conf import settings from nautobot.extras.plugins import PluginConfig - _CONFLICTING_APP_NAMES = [ # App names that conflict with nautobot_chatops "nautobot_plugin_chatops_aci", @@ -141,6 +141,7 @@ def ready(self): super().ready() # pylint: disable=import-outside-toplevel from nautobot_capacity_metrics import register_metric_func + from .metrics_app import metric_commands register_metric_func(metric_commands) diff --git a/nautobot_chatops/admin.py b/nautobot_chatops/admin.py index 3c4eecb4..4dc8f9a1 100644 --- a/nautobot_chatops/admin.py +++ b/nautobot_chatops/admin.py @@ -1,6 +1,7 @@ """Administrative capabilities for nautobot_chatops plugin.""" from django.contrib import admin + from .models import CommandLog diff --git a/nautobot_chatops/api/__init__.py b/nautobot_chatops/api/__init__.py index e69de29b..f5329948 100644 --- a/nautobot_chatops/api/__init__.py +++ b/nautobot_chatops/api/__init__.py @@ -0,0 +1 @@ +"""REST API module for nautobot_chatops app.""" diff --git a/nautobot_chatops/api/nested_serializers.py b/nautobot_chatops/api/nested_serializers.py index c933d039..60842e9e 100644 --- a/nautobot_chatops/api/nested_serializers.py +++ b/nautobot_chatops/api/nested_serializers.py @@ -1,8 +1,8 @@ """Nested Serializers for ChatOps Plugin.""" +from nautobot.core.api import WritableNestedSerializer from rest_framework import serializers -from nautobot.core.api import WritableNestedSerializer from nautobot_chatops.models import AccessGrant, CommandToken diff --git a/nautobot_chatops/api/serializers.py b/nautobot_chatops/api/serializers.py index 5427fdd4..ec6a8e3e 100644 --- a/nautobot_chatops/api/serializers.py +++ b/nautobot_chatops/api/serializers.py @@ -1,8 +1,7 @@ """API Serializers for ChatOps Plugin.""" -from rest_framework import serializers - from nautobot.core.api import ValidatedModelSerializer +from rest_framework import serializers from nautobot_chatops.models import AccessGrant, CommandToken diff --git a/nautobot_chatops/api/urls.py b/nautobot_chatops/api/urls.py index 2b24c136..78f4f66c 100644 --- a/nautobot_chatops/api/urls.py +++ b/nautobot_chatops/api/urls.py @@ -6,6 +6,7 @@ from django.conf import settings from django.urls import include, path from nautobot.core.api import OrderedDefaultRouter + from nautobot_chatops.api.views.generic import AccessGrantViewSet, CommandTokenViewSet, NautobotChatopsRootView from nautobot_chatops.api.views.lookup import AccessLookupView @@ -15,7 +16,7 @@ urlpatterns = [path("lookup/", AccessLookupView.as_view(), name="access_lookup")] if _APP_CONFIG.get("enable_slack"): - from nautobot_chatops.api.views.slack import SlackSlashCommandView, SlackInteractionView, SlackEventAPIView + from nautobot_chatops.api.views.slack import SlackEventAPIView, SlackInteractionView, SlackSlashCommandView urlpatterns += [ path("slack/slash_command/", SlackSlashCommandView.as_view(), name="slack_slash_command"), @@ -38,7 +39,7 @@ ] if _APP_CONFIG.get("enable_mattermost"): - from nautobot_chatops.api.views.mattermost import MattermostSlashCommandView, MattermostInteractionView + from nautobot_chatops.api.views.mattermost import MattermostInteractionView, MattermostSlashCommandView urlpatterns += [ path("mattermost/slash_command/", MattermostSlashCommandView.as_view(), name="mattermost_slash_command"), diff --git a/nautobot_chatops/api/views/generic.py b/nautobot_chatops/api/views/generic.py index c8c374ef..ae7c2b64 100644 --- a/nautobot_chatops/api/views/generic.py +++ b/nautobot_chatops/api/views/generic.py @@ -1,10 +1,11 @@ """API Views for Nautobot Chatops.""" -from rest_framework.routers import APIRootView + from nautobot.core.api.views import ModelViewSet +from rest_framework.routers import APIRootView from nautobot_chatops.api.serializers import AccessGrantSerializer, CommandTokenSerializer -from nautobot_chatops.models import AccessGrant, CommandToken from nautobot_chatops.filters import AccessGrantFilterSet, CommandTokenFilterSet +from nautobot_chatops.models import AccessGrant, CommandToken class NautobotChatopsRootView(APIRootView): diff --git a/nautobot_chatops/api/views/lookup.py b/nautobot_chatops/api/views/lookup.py index d20b9339..469ce141 100644 --- a/nautobot_chatops/api/views/lookup.py +++ b/nautobot_chatops/api/views/lookup.py @@ -1,6 +1,6 @@ """API views for dynamic lookup of platform-specific data.""" -from django.http import JsonResponse, HttpResponseBadRequest, HttpResponseNotFound +from django.http import HttpResponseBadRequest, HttpResponseNotFound, JsonResponse from django.views import View from nautobot_chatops.dispatchers import Dispatcher diff --git a/nautobot_chatops/api/views/mattermost.py b/nautobot_chatops/api/views/mattermost.py index a199499a..947ea1e1 100644 --- a/nautobot_chatops/api/views/mattermost.py +++ b/nautobot_chatops/api/views/mattermost.py @@ -10,12 +10,12 @@ from django.views import View from django.views.decorators.csrf import csrf_exempt -from nautobot_chatops.workers import get_commands_registry, commands_help, parse_command_string -from nautobot_chatops.dispatchers.mattermost import MattermostDispatcher, Driver -from nautobot_chatops.utils import check_and_enqueue_command +from nautobot_chatops.choices import CommandTokenPlatformChoices +from nautobot_chatops.dispatchers.mattermost import Driver, MattermostDispatcher from nautobot_chatops.metrics import signature_error_cntr from nautobot_chatops.models import CommandToken -from nautobot_chatops.choices import CommandTokenPlatformChoices +from nautobot_chatops.utils import check_and_enqueue_command +from nautobot_chatops.workers import commands_help, get_commands_registry, parse_command_string # pylint: disable=logging-fstring-interpolation diff --git a/nautobot_chatops/api/views/ms_teams.py b/nautobot_chatops/api/views/ms_teams.py index 7c256116..1b66a585 100644 --- a/nautobot_chatops/api/views/ms_teams.py +++ b/nautobot_chatops/api/views/ms_teams.py @@ -3,19 +3,17 @@ import json import re -import requests import jwt - +import requests from django.conf import settings from django.http import HttpResponse from django.utils.decorators import method_decorator from django.views import View from django.views.decorators.csrf import csrf_exempt -from nautobot_chatops.workers import get_commands_registry, commands_help, parse_command_string from nautobot_chatops.dispatchers.ms_teams import MSTeamsDispatcher from nautobot_chatops.utils import check_and_enqueue_command - +from nautobot_chatops.workers import commands_help, get_commands_registry, parse_command_string APP_ID = settings.PLUGINS_CONFIG["nautobot_chatops"]["microsoft_app_id"] diff --git a/nautobot_chatops/api/views/slack.py b/nautobot_chatops/api/views/slack.py index 9110bbdb..f56dff28 100644 --- a/nautobot_chatops/api/views/slack.py +++ b/nautobot_chatops/api/views/slack.py @@ -13,10 +13,10 @@ from django.views.decorators.csrf import csrf_exempt from slack_sdk import WebClient -from nautobot_chatops.workers import get_commands_registry, commands_help, parse_command_string from nautobot_chatops.dispatchers.slack import SlackDispatcher -from nautobot_chatops.utils import check_and_enqueue_command from nautobot_chatops.metrics import signature_error_cntr +from nautobot_chatops.utils import check_and_enqueue_command +from nautobot_chatops.workers import commands_help, get_commands_registry, parse_command_string # pylint: disable=logging-fstring-interpolation diff --git a/nautobot_chatops/api/views/webex.py b/nautobot_chatops/api/views/webex.py index 3c449aa3..33589667 100644 --- a/nautobot_chatops/api/views/webex.py +++ b/nautobot_chatops/api/views/webex.py @@ -11,14 +11,12 @@ from django.utils.decorators import method_decorator from django.views import View from django.views.decorators.csrf import csrf_exempt - from webexteamssdk import WebexTeamsAPI from webexteamssdk.exceptions import AccessTokenError, ApiError -from nautobot_chatops.workers import get_commands_registry, commands_help, parse_command_string -from nautobot_chatops.dispatchers.webex import WebexDispatcher -from nautobot_chatops.dispatchers.webex import WEBEX_CONFIG +from nautobot_chatops.dispatchers.webex import WEBEX_CONFIG, WebexDispatcher from nautobot_chatops.utils import check_and_enqueue_command +from nautobot_chatops.workers import commands_help, get_commands_registry, parse_command_string logger = logging.getLogger(__name__) diff --git a/nautobot_chatops/constants.py b/nautobot_chatops/constants.py index f12dedf9..a17d35db 100644 --- a/nautobot_chatops/constants.py +++ b/nautobot_chatops/constants.py @@ -13,7 +13,5 @@ # pylint: disable=line-too-long ACCESS_GRANT_VALUE_HELP_TEXT = "Corresponding ID value to grant access to.
Enter * to grant access to all organizations, channels, or users" -COMMAND_TOKEN_COMMENT_HELP_TEXT = "Optional: Enter description of token" # nosec - skips Bandit B105 error -COMMAND_TOKEN_TOKEN_HELP_TEXT = ( - "Token given by chat platform for signing or command validation" # nosec - skips Bandit B105 error -) +COMMAND_TOKEN_COMMENT_HELP_TEXT = "Optional: Enter description of token" # noqa: S105 +COMMAND_TOKEN_TOKEN_HELP_TEXT = "Token given by chat platform for signing or command validation" # noqa: S105 diff --git a/nautobot_chatops/dispatchers/adaptive_cards.py b/nautobot_chatops/dispatchers/adaptive_cards.py index d325c59d..d29b795f 100644 --- a/nautobot_chatops/dispatchers/adaptive_cards.py +++ b/nautobot_chatops/dispatchers/adaptive_cards.py @@ -1,4 +1,5 @@ """Dispatcher subclass for chat platforms that use Adaptive Cards (https://adaptivecards.io/).""" + from .base import Dispatcher # pylint: disable=abstract-method @@ -152,9 +153,7 @@ def prompt_for_text(self, action_id, help_text, label, title="Your attention ple blocks = [self.markdown_block(help_text), self.actions_block("TODO", [textentry, buttons])] return self.send_blocks(blocks, ephemeral=True, title=title) - def prompt_from_menu( - self, action_id, help_text, choices, default=(None, None), confirm=False, offset=0 - ): # pylint: disable=too-many-arguments + def prompt_from_menu(self, action_id, help_text, choices, default=(None, None), confirm=False, offset=0): # pylint: disable=too-many-arguments """Prompt the user to make a selection from a menu of choices. Args: @@ -201,7 +200,7 @@ def select_element(self, action_id, choices, default=(None, None), confirm=False Args: action_id (str): Identifying string to associate with this element choices (list): List of (display, value) tuples - default (tuple: Default (display, value) to preselect + default (tuple): Default (display, value) to preselect confirm (bool): If true (and the platform supports it), prompt the user to confirm their selection """ return { diff --git a/nautobot_chatops/dispatchers/base.py b/nautobot_chatops/dispatchers/base.py index a93b3ec1..13fcabee 100644 --- a/nautobot_chatops/dispatchers/base.py +++ b/nautobot_chatops/dispatchers/base.py @@ -1,10 +1,11 @@ """Generic base class modeling the API for sending messages to a generic chat platform.""" + import logging from typing import Dict, Optional -from django.templatetags.static import static -from django.core.cache import cache -from django.conf import settings +from django.conf import settings +from django.core.cache import cache +from django.templatetags.static import static from texttable import Texttable logger = logging.getLogger(__name__) @@ -93,16 +94,16 @@ def subclasses(cls): # TODO: this should be dynamic using entry_points # pylint: disable=import-outside-toplevel, unused-import, cyclic-import if _APP_CONFIG.get("enable_slack"): - from .slack import SlackDispatcher + from .slack import SlackDispatcher # noqa: F401 if _APP_CONFIG.get("enable_ms_teams"): - from .ms_teams import MSTeamsDispatcher + from .ms_teams import MSTeamsDispatcher # noqa: F401 if _APP_CONFIG.get("enable_webex"): - from .webex import WebexDispatcher + from .webex import WebexDispatcher # noqa: F401 if _APP_CONFIG.get("enable_mattermost"): - from .mattermost import MattermostDispatcher + from .mattermost import MattermostDispatcher # noqa: F401 subclasses = set() classes = [cls] @@ -268,9 +269,7 @@ def prompt_for_text(self, action_id, help_text, label, title="Your attention ple """ raise NotImplementedError - def prompt_from_menu( - self, action_id, help_text, choices, default=(None, None), confirm=False, offset=0 - ): # pylint: disable=too-many-arguments + def prompt_from_menu(self, action_id, help_text, choices, default=(None, None), confirm=False, offset=0): # pylint: disable=too-many-arguments """Prompt the user to make a selection from a menu of choices. Args: @@ -330,7 +329,7 @@ def select_element(self, action_id, choices, default=(None, None), confirm=False Args: action_id (str): Identifying string to associate with this element choices (list): List of (display, value) tuples - default (tuple: Default (display, value) to preselect + default (tuple): Default (display, value) to preselect confirm (bool): If true (and the platform supports it), prompt the user to confirm their selection """ raise NotImplementedError diff --git a/nautobot_chatops/dispatchers/mattermost.py b/nautobot_chatops/dispatchers/mattermost.py index ac74c83c..91768c9f 100644 --- a/nautobot_chatops/dispatchers/mattermost.py +++ b/nautobot_chatops/dispatchers/mattermost.py @@ -4,12 +4,13 @@ import logging import time from typing import Dict, Optional -import requests -from requests.exceptions import HTTPError +import requests from django.conf import settings +from requests.exceptions import HTTPError from nautobot_chatops.metrics import backend_action_sum + from .base import Dispatcher logger = logging.getLogger(__name__) @@ -476,9 +477,7 @@ def prompt_for_text(self, action_id, help_text, label, title="Your attention ple # In Mattermost, a textentry element can ONLY be sent in a modal Interactive dialog return self.send_blocks(blocks, callback_id=action_id, ephemeral=False, modal=True, title=title) - def prompt_from_menu( - self, action_id, help_text, choices, default=(None, None), confirm=False, offset=0 - ): # pylint: disable=too-many-arguments + def prompt_from_menu(self, action_id, help_text, choices, default=(None, None), confirm=False, offset=0): # pylint: disable=too-many-arguments """Prompt the user for a selection from a menu. Args: @@ -643,7 +642,7 @@ def select_element_interactive(self, action_id, choices, default=(None, None), c Args: action_id (str): Identifying string to associate with this element choices (list): List of (display, value) tuples - default (tuple: Default (display, value) to preselect + default (tuple): Default (display, value) to preselect confirm (bool): If true (and the platform supports it), prompt the user to confirm their selection optional (bool): If set to True, the field will return NoneType is not specified. diff --git a/nautobot_chatops/dispatchers/ms_teams.py b/nautobot_chatops/dispatchers/ms_teams.py index 0944b352..99b53eb7 100644 --- a/nautobot_chatops/dispatchers/ms_teams.py +++ b/nautobot_chatops/dispatchers/ms_teams.py @@ -1,12 +1,14 @@ """Dispatcher implementation for sending content to Microsoft Teams.""" -import os + import logging +import os from typing import Optional -import requests +import requests from django.conf import settings from nautobot_chatops.metrics import backend_action_sum + from .adaptive_cards import AdaptiveCardsDispatcher logger = logging.getLogger(__name__) diff --git a/nautobot_chatops/dispatchers/slack.py b/nautobot_chatops/dispatchers/slack.py index 93c846da..4584094e 100644 --- a/nautobot_chatops/dispatchers/slack.py +++ b/nautobot_chatops/dispatchers/slack.py @@ -4,15 +4,16 @@ import logging import os import time - from typing import Optional + from django.conf import settings from django.templatetags.static import static from slack_sdk import WebClient -from slack_sdk.webhook.client import WebhookClient from slack_sdk.errors import SlackApiError, SlackClientError +from slack_sdk.webhook.client import WebhookClient from nautobot_chatops.metrics import backend_action_sum + from .base import Dispatcher logger = logging.getLogger(__name__) @@ -353,9 +354,7 @@ def get_prompt_from_menu_choices(self, choices, offset=0): choices.append(("Next...", f"menu_offset-{new_offset}")) return choices - def prompt_from_menu( - self, action_id, help_text, choices, default=(None, None), confirm=False, offset=0 - ): # pylint: disable=too-many-arguments + def prompt_from_menu(self, action_id, help_text, choices, default=(None, None), confirm=False, offset=0): # pylint: disable=too-many-arguments """Prompt the user for a selection from a menu. Args: @@ -487,7 +486,7 @@ def select_element(self, action_id, choices, default=(None, None), confirm=False Args: action_id (str): Identifying string to associate with this element choices (list): List of (display, value) tuples - default (tuple: Default (display, value) to preselect + default (tuple): Default (display, value) to preselect confirm (bool): If true (and the platform supports it), prompt the user to confirm their selection """ data = { diff --git a/nautobot_chatops/dispatchers/webex.py b/nautobot_chatops/dispatchers/webex.py index fa9a93f9..9c8587f4 100644 --- a/nautobot_chatops/dispatchers/webex.py +++ b/nautobot_chatops/dispatchers/webex.py @@ -1,10 +1,11 @@ """Dispatcher implementation for sending content to Webex.""" -import logging +import logging from typing import Optional + +from texttable import Texttable from webexteamssdk import WebexTeamsAPI from webexteamssdk.exceptions import ApiError -from texttable import Texttable from nautobot_chatops.metrics import backend_action_sum from nautobot_chatops.utils import get_app_config_part diff --git a/nautobot_chatops/filters.py b/nautobot_chatops/filters.py index 919c050a..a8f2bd36 100644 --- a/nautobot_chatops/filters.py +++ b/nautobot_chatops/filters.py @@ -2,7 +2,7 @@ from nautobot.utilities.filters import BaseFilterSet -from nautobot_chatops.models import CommandLog, AccessGrant, CommandToken +from nautobot_chatops.models import AccessGrant, CommandLog, CommandToken class CommandLogFilterSet(BaseFilterSet): diff --git a/nautobot_chatops/forms.py b/nautobot_chatops/forms.py index 7264a594..f889fed5 100644 --- a/nautobot_chatops/forms.py +++ b/nautobot_chatops/forms.py @@ -1,12 +1,11 @@ """Forms for Nautobot.""" from django import forms - from nautobot.utilities.forms import BootstrapMixin -from .models import AccessGrant, CommandToken from .choices import AccessGrantTypeChoices, CommandTokenPlatformChoices from .constants import ACCESS_GRANT_COMMAND_HELP_TEXT, COMMAND_TOKEN_TOKEN_HELP_TEXT +from .models import AccessGrant, CommandToken BLANK_CHOICE = (("", "--------"),) diff --git a/nautobot_chatops/integrations/__init__.py b/nautobot_chatops/integrations/__init__.py index e69de29b..9c158ba0 100644 --- a/nautobot_chatops/integrations/__init__.py +++ b/nautobot_chatops/integrations/__init__.py @@ -0,0 +1 @@ +"""Nautobot ChatOps Integrations.""" diff --git a/nautobot_chatops/integrations/aci/aci.py b/nautobot_chatops/integrations/aci/aci.py index 247a4694..ccefc5c7 100644 --- a/nautobot_chatops/integrations/aci/aci.py +++ b/nautobot_chatops/integrations/aci/aci.py @@ -1,14 +1,14 @@ """All interactions with aci.""" -import sys import logging -from datetime import datetime -from datetime import timedelta import re +import sys +from datetime import datetime, timedelta + import requests import urllib3 -from .utils import tenant_from_dn, ap_from_dn +from .utils import ap_from_dn, tenant_from_dn urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) diff --git a/nautobot_chatops/integrations/aci/utils.py b/nautobot_chatops/integrations/aci/utils.py index 64869bdb..07a83049 100644 --- a/nautobot_chatops/integrations/aci/utils.py +++ b/nautobot_chatops/integrations/aci/utils.py @@ -1,6 +1,8 @@ """ACI ChatOps Utilities.""" + import logging import re + from prettytable import PrettyTable logger = logging.getLogger("nautobot") diff --git a/nautobot_chatops/integrations/aci/worker.py b/nautobot_chatops/integrations/aci/worker.py index ec788e5a..1ec9f980 100644 --- a/nautobot_chatops/integrations/aci/worker.py +++ b/nautobot_chatops/integrations/aci/worker.py @@ -1,13 +1,15 @@ """Worker functions implementing Nautobot "aci" command and subcommands.""" from distutils.util import strtobool + from django.conf import settings from nautobot.core.celery import nautobot_task from nautobot_chatops.choices import CommandStatusChoices -from nautobot_chatops.workers import subcommand_of, handle_subcommands -from .aci import RequestConnectError, RequestHTTPError, NautobotPluginChatopsAci -from .utils import logger, send_logo, build_table, send_wait_msg +from nautobot_chatops.workers import handle_subcommands, subcommand_of + +from .aci import NautobotPluginChatopsAci, RequestConnectError, RequestHTTPError +from .utils import build_table, logger, send_logo, send_wait_msg PLUGIN_SETTINGS = settings.PLUGINS_CONFIG["nautobot_chatops"] diff --git a/nautobot_chatops/integrations/ansible/tower.py b/nautobot_chatops/integrations/ansible/tower.py index 4f69ffdc..74138208 100644 --- a/nautobot_chatops/integrations/ansible/tower.py +++ b/nautobot_chatops/integrations/ansible/tower.py @@ -1,10 +1,11 @@ """All interactions with Ansible AWX/Tower.""" + import json import logging from urllib.parse import urlparse -from django.conf import settings import requests +from django.conf import settings logger = logging.getLogger("rq.worker") @@ -73,12 +74,12 @@ def _launch_job(self, template_name, extra_vars): url = f"{self.uri}/api/v2/job_templates/{template_name}/launch/" logger.info("Launch URL: %s", url) logger.info("Launch Extra Vars: %s", extra_vars) - response = requests.post( + response = requests.post( # noqa: S113 url, auth=requests.auth.HTTPBasicAuth(self.username, self.password), headers=self.headers, data=json.dumps({"extra_vars": extra_vars}), - verify=self.tower_verify_ssl, # nosec + verify=self.tower_verify_ssl, ) response.raise_for_status() logger.info("Job submission to Ansible Tower:") @@ -91,15 +92,16 @@ def _get_tower(self, api_path, **kwargs): Args: api_path (str): API path to get data from + **kwargs: Additional Keyword Arguments Returns: (JSON): JSON data for the response """ - response = requests.get( + response = requests.get( # noqa: S113 f"{self.uri}/api/v2/{api_path}", auth=(self.username, self.password), **kwargs, - verify=self.tower_verify_ssl, # nosec + verify=self.tower_verify_ssl, ) return response.json() @@ -152,7 +154,7 @@ def get_tower_inventory_hosts(self, group_id): """Gets hosts for a given Tower inventory. Args: - group (str): Group Name + group_id (str): Group Name Returns: (json): JSON data of the Tower hosts diff --git a/nautobot_chatops/integrations/ansible/worker.py b/nautobot_chatops/integrations/ansible/worker.py index e6abb6eb..c06df9d2 100644 --- a/nautobot_chatops/integrations/ansible/worker.py +++ b/nautobot_chatops/integrations/ansible/worker.py @@ -1,4 +1,5 @@ """Worker functions implementing Nautobot "ansible" command and subcommands.""" + import json import logging from collections import namedtuple @@ -6,6 +7,7 @@ import yaml from django.conf import settings from django_rq import job + from nautobot_chatops.workers import handle_subcommands, subcommand_of from .tower import Tower diff --git a/nautobot_chatops/integrations/aristacv/cvpgrpcutils.py b/nautobot_chatops/integrations/aristacv/cvpgrpcutils.py index 9e1d0da5..abf1e631 100644 --- a/nautobot_chatops/integrations/aristacv/cvpgrpcutils.py +++ b/nautobot_chatops/integrations/aristacv/cvpgrpcutils.py @@ -1,12 +1,13 @@ """Utilities for using GRPC with Cloudvision Chatbot.""" + import ssl -import requests -import grpc import arista.tag.v1 as tag +import grpc +import requests from google.protobuf import wrappers_pb2 as wrappers -from .utils import CVAAS_ADDR -from .utils import DEFAULT_TIMEOUT + +from .utils import CVAAS_ADDR, DEFAULT_TIMEOUT def connect_cv(settings): @@ -24,8 +25,10 @@ def connect_cv(settings): if insecure: cert = bytes(ssl.get_server_certificate((cvp_host, 8443)), "utf-8") channel_creds = grpc.ssl_channel_credentials(cert) - response = requests.post( - f"https://{cvp_host}/cvpservice/login/authenticate.do", auth=(username, password), verify=False # nosec + response = requests.post( # noqa: S113 + f"https://{cvp_host}/cvpservice/login/authenticate.do", + auth=(username, password), + verify=False, # noqa: S501 ) # Otherwise, the server is expected to have a valid certificate signed by a well-known CA. else: diff --git a/nautobot_chatops/integrations/aristacv/utils.py b/nautobot_chatops/integrations/aristacv/utils.py index 70ff1818..d1f91261 100644 --- a/nautobot_chatops/integrations/aristacv/utils.py +++ b/nautobot_chatops/integrations/aristacv/utils.py @@ -1,12 +1,14 @@ """Utilities for cloudvision chatbot.""" + import os import ssl from datetime import datetime + import requests -from google.protobuf.timestamp_pb2 import Timestamp # pylint: disable=no-name-in-module from cloudvision.Connector.grpc_client import GRPCClient, create_query from cvprac.cvp_client import CvpClient from django.conf import settings +from google.protobuf.timestamp_pb2 import Timestamp # pylint: disable=no-name-in-module fullpath = os.path.abspath(__file__) directory = os.path.dirname(fullpath) @@ -43,7 +45,7 @@ def get(name): CVP_HOST = CONFIG["cvp_host"] CVP_INSECURE = CONFIG["cvp_insecure"] ON_PREM = CONFIG["on_prem"] -CVP_TOKEN_PATH = "token.txt" # nosec +CVP_TOKEN_PATH = "token.txt" # noqa: S105 CRT_FILE_PATH = "cvp.crt" @@ -85,7 +87,7 @@ def connect_cvp(): clnt.connect([CVP_HOST], CVP_USERNAME, CVP_PASSWORD) else: clnt = CvpClient() - clnt.connect([cvaas_url], username="", password="", is_cvaas=True, api_token=CVAAS_TOKEN) # nosec + clnt.connect([cvaas_url], username="", password="", is_cvaas=True, api_token=CVAAS_TOKEN) return clnt @@ -556,10 +558,10 @@ def check_on_prem(): def get_token_crt(): """Writes cert and user token to files for onprem GRPClient use.""" if CVP_INSECURE.lower() == "true": - request = requests.post( + request = requests.post( # noqa: S113 f"https://{CVP_HOST}/cvpservice/login/authenticate.do", auth=(CVP_USERNAME, CVP_PASSWORD), - verify=False, # nosec + verify=False, # noqa: S501 ) else: request = requests.post( diff --git a/nautobot_chatops/integrations/aristacv/worker.py b/nautobot_chatops/integrations/aristacv/worker.py index 0a93d291..4815faae 100644 --- a/nautobot_chatops/integrations/aristacv/worker.py +++ b/nautobot_chatops/integrations/aristacv/worker.py @@ -1,39 +1,42 @@ """Cloudvision chatops.""" + import logging -from datetime import datetime, timedelta import os +from datetime import datetime, timedelta + from django_rq import job -from nautobot_chatops.workers import subcommand_of, handle_subcommands # pylint: disable=import-error + from nautobot_chatops.choices import CommandStatusChoices # pylint: disable=import-error +from nautobot_chatops.workers import handle_subcommands, subcommand_of # pylint: disable=import-error + from .cvpgrpcutils import get_device_tags from .utils import ( CONFIG, - prompt_for_events_filter, - prompt_for_device_or_container, + get_active_events_data, + get_active_events_data_filter, + get_active_severity_types, + get_applied_configlets_container_id, + get_applied_configlets_device_id, + get_bug_device_report, + get_bug_info, + get_cloudvision_configlets_names, get_cloudvision_container_devices, get_cloudvision_containers, - get_cloudvision_configlets_names, - get_configlet_config, get_cloudvision_devices_all, get_cloudvision_devices_all_resource, get_cloudvision_devices_by_sn, - get_device_id_from_hostname, - get_device_running_configuration, - get_cloudvision_tasks, get_cloudvision_task_logs, + get_cloudvision_tasks, + get_configlet_config, get_container_id_by_name, - get_applied_configlets_container_id, - get_applied_configlets_device_id, - get_severity_choices, - get_active_events_data, - get_active_events_data_filter, - get_active_severity_types, get_device_bugs_data, - get_bug_info, - get_bug_device_report, + get_device_id_from_hostname, + get_device_running_configuration, + get_severity_choices, + prompt_for_device_or_container, + prompt_for_events_filter, ) - logger = logging.getLogger("rq.worker") dir_path = os.path.dirname(os.path.realpath(__file__)) CLOUDVISION_LOGO_PATH = "nautobot_cloudvision/cloudvision_logo.png" diff --git a/nautobot_chatops/integrations/grafana/__init__.py b/nautobot_chatops/integrations/grafana/__init__.py index e69de29b..10a07da3 100644 --- a/nautobot_chatops/integrations/grafana/__init__.py +++ b/nautobot_chatops/integrations/grafana/__init__.py @@ -0,0 +1 @@ +"""Nautobot ChatOps Graphana Integration.""" diff --git a/nautobot_chatops/integrations/grafana/api/urls.py b/nautobot_chatops/integrations/grafana/api/urls.py index c6b4e88f..70258352 100644 --- a/nautobot_chatops/integrations/grafana/api/urls.py +++ b/nautobot_chatops/integrations/grafana/api/urls.py @@ -1,10 +1,9 @@ """Django urlpatterns declaration for nautobot_chatops.integrations.grafana plugin.""" from django.conf import settings - from nautobot.core.api import OrderedDefaultRouter -from nautobot_chatops.integrations.grafana.api.views.generic import NautobotPluginChatopsGrafanaRootView +from nautobot_chatops.integrations.grafana.api.views.generic import NautobotPluginChatopsGrafanaRootView urlpatterns = [] if settings.PLUGINS_CONFIG["nautobot_chatops"]["enable_grafana"]: diff --git a/nautobot_chatops/integrations/grafana/api/views/generic.py b/nautobot_chatops/integrations/grafana/api/views/generic.py index 4fdad892..5700b664 100644 --- a/nautobot_chatops/integrations/grafana/api/views/generic.py +++ b/nautobot_chatops/integrations/grafana/api/views/generic.py @@ -1,4 +1,5 @@ """API Views for Nautobot Plugin Chatops Grafana.""" + from rest_framework.routers import APIRootView diff --git a/nautobot_chatops/integrations/grafana/diffsync/models.py b/nautobot_chatops/integrations/grafana/diffsync/models.py index 772d7f08..7f0180ec 100644 --- a/nautobot_chatops/integrations/grafana/diffsync/models.py +++ b/nautobot_chatops/integrations/grafana/diffsync/models.py @@ -1,8 +1,11 @@ """DiffSync model definitions for Grafana Dashboards.""" -from typing import Optional, List + +from typing import List, Optional + from diffsync import DiffSync, DiffSyncModel -from nautobot_chatops.integrations.grafana.models import Dashboard, Panel, PanelVariable + from nautobot_chatops.integrations.grafana.helpers import format_command +from nautobot_chatops.integrations.grafana.models import Dashboard, Panel, PanelVariable class DashboardModel(DiffSyncModel): diff --git a/nautobot_chatops/integrations/grafana/diffsync/sync.py b/nautobot_chatops/integrations/grafana/diffsync/sync.py index 45c1fc68..b7b00149 100644 --- a/nautobot_chatops/integrations/grafana/diffsync/sync.py +++ b/nautobot_chatops/integrations/grafana/diffsync/sync.py @@ -1,16 +1,19 @@ """Synchronization functions for the implemented DiffSync models.""" + from typing import Union + from diffsync import DiffSyncFlags -from nautobot_chatops.integrations.grafana.grafana import handler -from nautobot_chatops.integrations.grafana.models import Dashboard, Panel, PanelVariable + from nautobot_chatops.integrations.grafana.diffsync.models import ( - NautobotDashboard, GrafanaDashboard, - NautobotPanel, GrafanaPanel, - NautobotVariable, GrafanaVariable, + NautobotDashboard, + NautobotPanel, + NautobotVariable, ) +from nautobot_chatops.integrations.grafana.grafana import handler +from nautobot_chatops.integrations.grafana.models import Dashboard, Panel, PanelVariable def run_dashboard_sync(overwrite: bool = False) -> Union[str, None]: diff --git a/nautobot_chatops/integrations/grafana/exceptions.py b/nautobot_chatops/integrations/grafana/exceptions.py index 6763f514..91fecb57 100644 --- a/nautobot_chatops/integrations/grafana/exceptions.py +++ b/nautobot_chatops/integrations/grafana/exceptions.py @@ -1,6 +1,7 @@ """Nautobot Plugin ChatOps Grafana Exceptions.""" -from pydantic import ValidationError + from isodate import ISO8601Error +from pydantic import ValidationError class DefaultArgsError(BaseException): diff --git a/nautobot_chatops/integrations/grafana/filters.py b/nautobot_chatops/integrations/grafana/filters.py index be7bf0c0..733dccd1 100644 --- a/nautobot_chatops/integrations/grafana/filters.py +++ b/nautobot_chatops/integrations/grafana/filters.py @@ -1,7 +1,7 @@ """Filtering for nautobot_plugin_device_lifecycle_mgmt UI.""" -from django_filters import FilterSet, CharFilter from django.db.models import Q +from django_filters import CharFilter, FilterSet from nautobot_chatops.integrations.grafana.models import Dashboard, Panel, PanelVariable diff --git a/nautobot_chatops/integrations/grafana/forms.py b/nautobot_chatops/integrations/grafana/forms.py index e14fc1b9..db50ac98 100644 --- a/nautobot_chatops/integrations/grafana/forms.py +++ b/nautobot_chatops/integrations/grafana/forms.py @@ -1,19 +1,19 @@ """Forms for Nautobot.""" +from django.core.serializers.json import DjangoJSONEncoder from django.forms import ( - ModelForm, + BooleanField, CharField, IntegerField, - BooleanField, JSONField, ModelChoiceField, + ModelForm, ModelMultipleChoiceField, MultipleHiddenInput, ) -from django.core.serializers.json import DjangoJSONEncoder -from nautobot.utilities.forms import BootstrapMixin from nautobot.extras.forms import CustomFieldModelCSVForm -from nautobot.utilities.forms import BulkEditForm +from nautobot.utilities.forms import BootstrapMixin, BulkEditForm + from nautobot_chatops.integrations.grafana.models import Dashboard, Panel, PanelVariable diff --git a/nautobot_chatops/integrations/grafana/grafana.py b/nautobot_chatops/integrations/grafana/grafana.py index 36dd280a..e8031ce5 100644 --- a/nautobot_chatops/integrations/grafana/grafana.py +++ b/nautobot_chatops/integrations/grafana/grafana.py @@ -1,15 +1,17 @@ """This module is intended to handle grafana requests generically perhaps outside of nautobot.""" + import datetime import logging import urllib.parse -from typing import Union, Tuple, List -import requests -import isodate +from typing import List, Tuple, Union +import isodate +import requests from django.conf import settings from pydantic import BaseModel # pylint: disable=no-name-in-module from requests.exceptions import RequestException from typing_extensions import Literal + from nautobot_chatops.integrations.grafana.models import Panel, PanelVariable LOGGER = logging.getLogger("nautobot.plugin.grafana") diff --git a/nautobot_chatops/integrations/grafana/helpers.py b/nautobot_chatops/integrations/grafana/helpers.py index 67e3f551..08d551f3 100644 --- a/nautobot_chatops/integrations/grafana/helpers.py +++ b/nautobot_chatops/integrations/grafana/helpers.py @@ -1,11 +1,12 @@ """Schema Enforcer wrapper used to mimic the validate cli functionality.""" + from typing import List -from termcolor import colored from schema_enforcer import config -from schema_enforcer.schemas.manager import SchemaManager -from schema_enforcer.instances.file import InstanceFileManager from schema_enforcer.exceptions import InvalidJSONSchema +from schema_enforcer.instances.file import InstanceFileManager +from schema_enforcer.schemas.manager import SchemaManager +from termcolor import colored SPECIAL_CHAR = { "%": "percent", diff --git a/nautobot_chatops/integrations/grafana/management/commands/gen_panels_from_dashboard.py b/nautobot_chatops/integrations/grafana/management/commands/gen_panels_from_dashboard.py index 1600cf6c..c3c7d573 100644 --- a/nautobot_chatops/integrations/grafana/management/commands/gen_panels_from_dashboard.py +++ b/nautobot_chatops/integrations/grafana/management/commands/gen_panels_from_dashboard.py @@ -1,9 +1,10 @@ """Generate a panels.yml file using the json file retrieved from Grafana.""" + import json -import yaml -from termcolor import colored +import yaml from django.core.management.base import BaseCommand +from termcolor import colored class Command(BaseCommand): diff --git a/nautobot_chatops/integrations/grafana/management/commands/import_panels.py b/nautobot_chatops/integrations/grafana/management/commands/import_panels.py index 77307798..ecbc7a2a 100644 --- a/nautobot_chatops/integrations/grafana/management/commands/import_panels.py +++ b/nautobot_chatops/integrations/grafana/management/commands/import_panels.py @@ -1,9 +1,10 @@ """Import a panels.yml file into the Grafana object models in Nautobot.""" -import yaml -from termcolor import colored +import yaml from django.core.management.base import BaseCommand from pydantic import ValidationError +from termcolor import colored + from nautobot_chatops.integrations.grafana.helpers import validate from nautobot_chatops.integrations.grafana.models import Dashboard, Panel, PanelVariable diff --git a/nautobot_chatops/integrations/grafana/management/commands/validate_schema.py b/nautobot_chatops/integrations/grafana/management/commands/validate_schema.py index 02ada2ef..e50080dd 100644 --- a/nautobot_chatops/integrations/grafana/management/commands/validate_schema.py +++ b/nautobot_chatops/integrations/grafana/management/commands/validate_schema.py @@ -1,5 +1,7 @@ """Nautobot Server CLI extension for Grafana ChatOps.""" + from django.core.management.base import BaseCommand + from nautobot_chatops.integrations.grafana.helpers import validate diff --git a/nautobot_chatops/integrations/grafana/models.py b/nautobot_chatops/integrations/grafana/models.py index 9087eac8..05747593 100644 --- a/nautobot_chatops/integrations/grafana/models.py +++ b/nautobot_chatops/integrations/grafana/models.py @@ -1,10 +1,11 @@ """Models for Grafana Plugin.""" -from django.db import models + from django.core.exceptions import ValidationError from django.core.serializers.json import DjangoJSONEncoder +from django.db import models from django.utils.translation import gettext_lazy as _ from nautobot.circuits import models as circuit_models -from nautobot.core.models.generics import PrimaryModel, OrganizationalModel +from nautobot.core.models.generics import OrganizationalModel, PrimaryModel from nautobot.dcim import models as dcim_models from nautobot.extras import models as extra_models from nautobot.extras.utils import extras_features @@ -12,7 +13,6 @@ from nautobot.tenancy import models as tenancy_models from nautobot.virtualization import models as virtualization_models - # Valid models to be used in Panel Variables as query options. If a model doesn't exist in # this list, you cannot set or use the `query` field in a panel variable. VALID_MODELS = ( diff --git a/nautobot_chatops/integrations/grafana/navigation.py b/nautobot_chatops/integrations/grafana/navigation.py index 6031018a..74d9ee88 100644 --- a/nautobot_chatops/integrations/grafana/navigation.py +++ b/nautobot_chatops/integrations/grafana/navigation.py @@ -1,5 +1,6 @@ """Navigation for Circuit Maintenance.""" -from nautobot.extras.plugins import PluginMenuItem, PluginMenuButton, ButtonColorChoices + +from nautobot.extras.plugins import ButtonColorChoices, PluginMenuButton, PluginMenuItem menu_items = ( PluginMenuItem( diff --git a/nautobot_chatops/integrations/grafana/tables.py b/nautobot_chatops/integrations/grafana/tables.py index abb4d39f..9f302fb0 100644 --- a/nautobot_chatops/integrations/grafana/tables.py +++ b/nautobot_chatops/integrations/grafana/tables.py @@ -1,8 +1,9 @@ """Django table classes for Nautobot.""" -from django_tables2 import TemplateColumn, Column, BooleanColumn -from nautobot.utilities.tables import BaseTable, ToggleColumn, ButtonsColumn -from nautobot_chatops.integrations.grafana.models import Panel, Dashboard, PanelVariable +from django_tables2 import BooleanColumn, Column, TemplateColumn +from nautobot.utilities.tables import BaseTable, ButtonsColumn, ToggleColumn + +from nautobot_chatops.integrations.grafana.models import Dashboard, Panel, PanelVariable class DashboardViewTable(BaseTable): diff --git a/nautobot_chatops/integrations/grafana/urls.py b/nautobot_chatops/integrations/grafana/urls.py index 96c67b7e..4371c727 100644 --- a/nautobot_chatops/integrations/grafana/urls.py +++ b/nautobot_chatops/integrations/grafana/urls.py @@ -1,32 +1,34 @@ """Django urlpatterns declaration for nautobot_chatops.integrations.grafana plugin.""" + from django.urls import path from nautobot.extras.views import ObjectChangeLogView -from nautobot_chatops.integrations.grafana.models import Dashboard, PanelVariable, Panel + +from nautobot_chatops.integrations.grafana.models import Dashboard, Panel, PanelVariable from nautobot_chatops.integrations.grafana.views import ( + DashboardBulkEditView, Dashboards, + DashboardsBulkDeleteView, + DashboardsBulkImportView, DashboardsCreate, DashboardsDelete, DashboardsEdit, DashboardsSync, - DashboardsBulkImportView, - DashboardsBulkDeleteView, - DashboardBulkEditView, Panels, + PanelsBulkDeleteView, + PanelsBulkEditView, + PanelsBulkImportView, PanelsCreate, + PanelsDelete, PanelsEdit, PanelsSync, - PanelsDelete, - PanelsBulkImportView, - PanelsBulkDeleteView, - PanelsBulkEditView, Variables, - VariablesCreate, - VariablesSync, - VariablesEdit, - VariablesDelete, - VariablesBulkImportView, VariablesBulkDeleteView, VariablesBulkEditView, + VariablesBulkImportView, + VariablesCreate, + VariablesDelete, + VariablesEdit, + VariablesSync, ) urlpatterns = [ diff --git a/nautobot_chatops/integrations/grafana/views.py b/nautobot_chatops/integrations/grafana/views.py index 2b0dfba5..5b60734e 100644 --- a/nautobot_chatops/integrations/grafana/views.py +++ b/nautobot_chatops/integrations/grafana/views.py @@ -4,39 +4,40 @@ to send requests and notifications to. """ -from django.shortcuts import render, reverse, redirect from django.contrib import messages from django.contrib.auth.mixins import PermissionRequiredMixin +from django.shortcuts import redirect, render, reverse from nautobot.core.views.generic import ( - ObjectEditView, - ObjectDeleteView, - ObjectListView, - BulkImportView, BulkDeleteView, BulkEditView, + BulkImportView, + ObjectDeleteView, + ObjectEditView, + ObjectListView, ) from nautobot.utilities.forms import ConfirmationForm + from nautobot_chatops.integrations.grafana.diffsync.sync import run_dashboard_sync, run_panels_sync, run_variables_sync -from nautobot_chatops.integrations.grafana.tables import PanelViewTable, DashboardViewTable, PanelVariableViewTable -from nautobot_chatops.integrations.grafana.models import Panel, Dashboard, PanelVariable -from nautobot_chatops.integrations.grafana.grafana import handler from nautobot_chatops.integrations.grafana.filters import DashboardFilter, PanelFilter, VariableFilter from nautobot_chatops.integrations.grafana.forms import ( - DashboardsForm, - DashboardsFilterForm, - DashboardCSVForm, DashboardBulkEditForm, - PanelsForm, - PanelsSyncForm, + DashboardCSVForm, + DashboardsFilterForm, + DashboardsForm, + PanelsBulkEditForm, PanelsCSVForm, PanelsFilterForm, - PanelsBulkEditForm, - PanelVariablesForm, - PanelVariablesSyncForm, - PanelVariablesFilterForm, + PanelsForm, + PanelsSyncForm, PanelVariablesBulkEditForm, PanelVariablesCSVForm, + PanelVariablesFilterForm, + PanelVariablesForm, + PanelVariablesSyncForm, ) +from nautobot_chatops.integrations.grafana.grafana import handler +from nautobot_chatops.integrations.grafana.models import Dashboard, Panel, PanelVariable +from nautobot_chatops.integrations.grafana.tables import DashboardViewTable, PanelVariableViewTable, PanelViewTable # ------------------------------------------------------------------------------------- # Dashboard Specific Views diff --git a/nautobot_chatops/integrations/grafana/worker.py b/nautobot_chatops/integrations/grafana/worker.py index 88226377..76d06fe5 100644 --- a/nautobot_chatops/integrations/grafana/worker.py +++ b/nautobot_chatops/integrations/grafana/worker.py @@ -1,27 +1,30 @@ """Worker function for /net commands in Slack.""" -import tempfile + import argparse import os +import tempfile from datetime import datetime -from typing import NoReturn, List, Union, Dict +from typing import Dict, List, NoReturn, Union + +from django.core.exceptions import FieldError, MultipleObjectsReturned, ObjectDoesNotExist +from django_rq import job from isodate import ISO8601Error, parse_duration from jinja2 import Template -from django_rq import job -from django.core.exceptions import FieldError, ObjectDoesNotExist, MultipleObjectsReturned -from pydantic.error_wrappers import ValidationError # pylint: disable=no-name-in-module from nautobot.utilities.querysets import RestrictedQuerySet +from pydantic.error_wrappers import ValidationError # pylint: disable=no-name-in-module + from nautobot_chatops.dispatchers import Dispatcher -from nautobot_chatops.workers import handle_subcommands, add_subcommand -from nautobot_chatops.integrations.grafana.models import Panel, PanelVariable, VALID_MODELS +from nautobot_chatops.integrations.grafana.exceptions import DefaultArgsError, MultipleOptionsError, PanelError from nautobot_chatops.integrations.grafana.grafana import ( - SLASH_COMMAND, - LOGGER, - GRAFANA_LOGO_PATH, GRAFANA_LOGO_ALT, + GRAFANA_LOGO_PATH, + LOGGER, REQUEST_TIMEOUT_SEC, + SLASH_COMMAND, handler, ) -from nautobot_chatops.integrations.grafana.exceptions import DefaultArgsError, PanelError, MultipleOptionsError +from nautobot_chatops.integrations.grafana.models import VALID_MODELS, Panel, PanelVariable +from nautobot_chatops.workers import add_subcommand, handle_subcommands def grafana_logo(dispatcher): @@ -70,6 +73,7 @@ def chat_get_panel(dispatcher: Dispatcher, *args) -> bool: # pylint: disable=to Args: dispatcher (nautobot_chatops.dispatchers.Dispatcher): Abstracted dispatcher class for chat-ops. + *args: Grafana Panel Arguments. Returns: bool: ChatOps response pass or fail. @@ -121,6 +125,7 @@ def chat_parse_args(panel_vars: List[PanelVariable], *args) -> Union[dict, bool] Args: panel_vars (List[nautobot_chatops.models.GrafanaPanelVariable]): List of PanelVariable objects. + *args: Additional Arguments. Returns: parsed_args: dict of the arguments from the user's raw input diff --git a/nautobot_chatops/integrations/ipfabric/context.py b/nautobot_chatops/integrations/ipfabric/context.py index 2cff7ab2..a53a344a 100644 --- a/nautobot_chatops/integrations/ipfabric/context.py +++ b/nautobot_chatops/integrations/ipfabric/context.py @@ -1,13 +1,16 @@ """Functions for caching per-user context.""" + import hashlib + from django.core.cache import cache + from nautobot_chatops import NautobotChatOpsConfig def _get_cache_key(user: str) -> str: """Key generator for the cache, adding the plugin prefix name.""" key_string = "-".join([NautobotChatOpsConfig.name, user]) - return hashlib.md5(key_string.encode("utf-8")).hexdigest() # nosec + return hashlib.md5(key_string.encode("utf-8")).hexdigest() # noqa: S324 def get_context(user: str) -> dict: diff --git a/nautobot_chatops/integrations/ipfabric/ipfabric_wrapper.py b/nautobot_chatops/integrations/ipfabric/ipfabric_wrapper.py index d8194dcd..f34a4c81 100644 --- a/nautobot_chatops/integrations/ipfabric/ipfabric_wrapper.py +++ b/nautobot_chatops/integrations/ipfabric/ipfabric_wrapper.py @@ -2,9 +2,8 @@ import logging -from ipfabric_diagrams import IPFDiagram from ipfabric import IPFClient - +from ipfabric_diagrams import IPFDiagram logger = logging.getLogger("nautobot") diff --git a/nautobot_chatops/integrations/ipfabric/worker.py b/nautobot_chatops/integrations/ipfabric/worker.py index 9f4ab94f..6cf057d2 100644 --- a/nautobot_chatops/integrations/ipfabric/worker.py +++ b/nautobot_chatops/integrations/ipfabric/worker.py @@ -1,7 +1,8 @@ """Worker functions implementing Nautobot "ipfabric" command and subcommands.""" # pylint: disable=too-many-lines + import logging -import tempfile import os +import tempfile from datetime import datetime from django.conf import settings @@ -12,11 +13,10 @@ from pkg_resources import parse_version from nautobot_chatops.choices import CommandStatusChoices -from nautobot_chatops.workers import subcommand_of, handle_subcommands - -from .ipfabric_wrapper import IpFabric +from nautobot_chatops.workers import handle_subcommands, subcommand_of from .context import get_context, set_context +from .ipfabric_wrapper import IpFabric from .utils import parse_hosts BASE_CMD = "ipfabric" @@ -431,9 +431,7 @@ def get_int_drops(dispatcher, device, snapshot_id): # PATH LOOKUP COMMMAND -def submit_pathlookup( - dispatcher, sub_cmd, src_ip, dst_ip, protocol, src_port=None, dst_port=None, icmp_type=None -): # pylint: disable=too-many-arguments, too-many-locals +def submit_pathlookup(dispatcher, sub_cmd, src_ip, dst_ip, protocol, src_port=None, dst_port=None, icmp_type=None): # pylint: disable=too-many-arguments, too-many-locals """Path simulation diagram lookup between source and target IP address.""" snapshot_id = get_user_snapshot(dispatcher) # diagrams for 4.0 - 4.2 are not supported due to attribute changes in 4.3+ @@ -488,9 +486,7 @@ def submit_pathlookup( @subcommand_of("ipfabric") -def pathlookup( - dispatcher, src_ip, dst_ip, src_port, dst_port, protocol -): # pylint: disable=too-many-arguments, too-many-locals +def pathlookup(dispatcher, src_ip, dst_ip, src_port, dst_port, protocol): # pylint: disable=too-many-arguments, too-many-locals """Path simulation diagram lookup between source and target IP address.""" sub_cmd = "pathlookup" supported_protocols = ["tcp", "udp"] @@ -978,9 +974,7 @@ def find_host(dispatcher, filter_key=None, filter_value=None): @subcommand_of("ipfabric") -def table_diff( - dispatcher, category, table, view, snapshot -): # pylint: disable=too-many-return-statements, too-many-branches +def table_diff(dispatcher, category, table, view, snapshot): # pylint: disable=too-many-return-statements, too-many-branches """Get difference of a table between the current snapshot and the specified snapshot.""" sub_cmd = "table-diff" diff --git a/nautobot_chatops/integrations/meraki/worker.py b/nautobot_chatops/integrations/meraki/worker.py index 0e1ac7fb..8f8dfed4 100644 --- a/nautobot_chatops/integrations/meraki/worker.py +++ b/nautobot_chatops/integrations/meraki/worker.py @@ -1,15 +1,16 @@ """Demo meraki addition to Nautobot.""" -import os + import logging +import os from django.conf import settings from django_rq import job -from nautobot_chatops.workers import subcommand_of, handle_subcommands + from nautobot_chatops.choices import CommandStatusChoices +from nautobot_chatops.workers import handle_subcommands, subcommand_of from .utils import MerakiClient - MERAKI_LOGO_PATH = "nautobot_meraki/meraki.png" MERAKI_LOGO_ALT = "Meraki Logo" diff --git a/nautobot_chatops/integrations/panorama/constant.py b/nautobot_chatops/integrations/panorama/constant.py index 17967d43..3145cf55 100644 --- a/nautobot_chatops/integrations/panorama/constant.py +++ b/nautobot_chatops/integrations/panorama/constant.py @@ -1,4 +1,5 @@ """Storage of data that will not change throughout the life cycle of application.""" + from django.conf import settings PLUGIN_CFG = settings.PLUGINS_CONFIG["nautobot_chatops"] diff --git a/nautobot_chatops/integrations/panorama/jinja_filters.py b/nautobot_chatops/integrations/panorama/jinja_filters.py index ac7798c9..c3d0df3f 100644 --- a/nautobot_chatops/integrations/panorama/jinja_filters.py +++ b/nautobot_chatops/integrations/panorama/jinja_filters.py @@ -1,4 +1,5 @@ """Custom Django Jinja filters.""" + from django_jinja import library diff --git a/nautobot_chatops/integrations/panorama/utils.py b/nautobot_chatops/integrations/panorama/utils.py index e8157d9a..b015a767 100644 --- a/nautobot_chatops/integrations/panorama/utils.py +++ b/nautobot_chatops/integrations/panorama/utils.py @@ -1,20 +1,20 @@ """Functions used for interacting with Panroama.""" + import logging import time from typing import List + import defusedxml.ElementTree as ET import requests - from netmiko import ConnectHandler from panos.errors import PanDeviceXapiError from panos.firewall import Firewall -from panos.panorama import Panorama, DeviceGroup, PanoramaDeviceGroupHierarchy +from panos.panorama import DeviceGroup, Panorama, PanoramaDeviceGroupHierarchy from panos.policies import PostRulebase, PreRulebase, Rulebase, SecurityRule from requests.exceptions import RequestException from .constant import PLUGIN_CFG - logger = logging.getLogger(__name__) @@ -31,7 +31,7 @@ def get_api_key_api(url: str = PLUGIN_CFG["panorama_host"]) -> str: params = {"type": "keygen", "user": PLUGIN_CFG["panorama_user"], "password": PLUGIN_CFG["panorama_password"]} - response = requests.get(f"https://{url}/api/", params=params, verify=False) # nosec + response = requests.get(f"https://{url}/api/", params=params, verify=False) # noqa: S113,S501 if response.status_code != 200: raise RequestException(f"Something went wrong while making a request. Reason: {response.text}") @@ -77,6 +77,8 @@ def get_from_pano(connection: Panorama, devices: bool = False, groups: bool = Fa Args: connection (Panorama): Connection object to Panorama. + devices (bool): Get Devices from Panorama. + groups (bool): Get Groups from Panorama. Returns: dict: Dictionary of all devices attached to Panorama. @@ -176,12 +178,12 @@ def start_packet_capture(capture_filename: str, ip_address: str, filters: dict): if filters["dport"] and filters["dport"] != "any": command += f" destination-port {filters['dport']}" - if filters["dnet"] != "0.0.0.0": # nosec + if filters["dnet"] != "0.0.0.0": # noqa: S104 command += f" destination {filters['dnet']}" if filters["dcidr"] != "0": command += f" destination-netmask {filters['dcidr']}" - if filters["snet"] != "0.0.0.0": # nosec + if filters["snet"] != "0.0.0.0": # noqa: S104 command += f" source {filters['snet']}" if filters["scidr"] != "0": command += f" source-netmask {filters['scidr']}" @@ -218,10 +220,10 @@ def _get_pcap(capture_filename: str, ip_address: str): params = {"key": get_api_key_api(), "type": "export", "category": "filters-pcap", "from": "1.pcap"} - respone = requests.get(url, params=params, verify=False) # nosec + response = requests.get(url, params=params, verify=False) # noqa: S113,S501 with open(capture_filename, "wb") as pcap_file: - pcap_file.write(respone.content) + pcap_file.write(response.content) def parse_all_rule_names(xml_rules: str) -> list: diff --git a/nautobot_chatops/integrations/panorama/worker.py b/nautobot_chatops/integrations/panorama/worker.py index 1c7e676f..dd75fb29 100644 --- a/nautobot_chatops/integrations/panorama/worker.py +++ b/nautobot_chatops/integrations/panorama/worker.py @@ -1,4 +1,5 @@ """Example rq worker to handle /panorama chat commands with 1 subcommand addition.""" + import logging import os import re @@ -16,13 +17,13 @@ from nautobot_chatops.choices import CommandStatusChoices from nautobot_chatops.integrations.panorama.utils import ( connect_panorama, - get_from_pano, - get_rule_match, - start_packet_capture, get_all_rules, - split_rules, + get_from_pano, get_object, get_panorama_device_group_hierarchy, + get_rule_match, + split_rules, + start_packet_capture, ) from nautobot_chatops.workers import handle_subcommands, subcommand_of @@ -176,9 +177,7 @@ def get_devicegroups(dispatcher, **kwargs): @subcommand_of("panorama") -def validate_rule_exists( - dispatcher, device, src_ip, dst_ip, protocol, dst_port -): # pylint:disable=too-many-arguments,too-many-locals,too-many-branches,too-many-statements +def validate_rule_exists(dispatcher, device, src_ip, dst_ip, protocol, dst_port): # pylint:disable=too-many-arguments,too-many-locals,too-many-branches,too-many-statements """Verify that the rule exists within a device, via Panorama.""" dialog_list = [ { @@ -576,6 +575,7 @@ def capture_traffic( ip_proto (str): Protocol for destination port stage (str): Stage to use capture_seconds (str): Number of seconds to run packet capture + **kwargs: Addition Keyword Arguments """ logger.info("Starting capture_traffic()") diff --git a/nautobot_chatops/integrations/utils.py b/nautobot_chatops/integrations/utils.py index 587e51b0..9469d268 100644 --- a/nautobot_chatops/integrations/utils.py +++ b/nautobot_chatops/integrations/utils.py @@ -1,4 +1,5 @@ """Utility functions for nautobot_chatops integrations.""" + from collections.abc import Mapping from importlib import import_module from pathlib import Path diff --git a/nautobot_chatops/management/commands/start_slack_socket.py b/nautobot_chatops/management/commands/start_slack_socket.py index 0c8fb3f2..5a029488 100644 --- a/nautobot_chatops/management/commands/start_slack_socket.py +++ b/nautobot_chatops/management/commands/start_slack_socket.py @@ -1,7 +1,9 @@ """Command to start a slack socket.""" import asyncio + from django.core.management.base import BaseCommand + from nautobot_chatops.sockets.slack import main diff --git a/nautobot_chatops/migrations/0001_initial.py b/nautobot_chatops/migrations/0001_initial.py index 68561b79..52cfd7d8 100644 --- a/nautobot_chatops/migrations/0001_initial.py +++ b/nautobot_chatops/migrations/0001_initial.py @@ -1,9 +1,10 @@ # Generated by Django 3.1.3 on 2021-02-22 02:18 +import uuid + import django.contrib.postgres.fields -from django.db import migrations, models, connection import nautobot.utilities.fields -import uuid +from django.db import connection, migrations, models class Migration(migrations.Migration): diff --git a/nautobot_chatops/migrations/0002_commandlog_params1.py b/nautobot_chatops/migrations/0002_commandlog_params1.py index 9e8789ed..395f96cb 100644 --- a/nautobot_chatops/migrations/0002_commandlog_params1.py +++ b/nautobot_chatops/migrations/0002_commandlog_params1.py @@ -1,8 +1,7 @@ # Generated by Django 3.1.12 on 2021-08-14 02:29 from django.db import migrations, models -import nautobot_chatops.models - +import nautobot_chatops.models # noqa: F401 class Migration(migrations.Migration): dependencies = [ diff --git a/nautobot_chatops/migrations/0003_params_to_params1.py b/nautobot_chatops/migrations/0003_params_to_params1.py index aae448b0..5001056d 100644 --- a/nautobot_chatops/migrations/0003_params_to_params1.py +++ b/nautobot_chatops/migrations/0003_params_to_params1.py @@ -1,7 +1,6 @@ import copy -import json -from django.db import migrations, connection +from django.db import connection, migrations def migrate_params(apps, schema_editor): diff --git a/nautobot_chatops/migrations/0004_remove_params_rename_params1.py b/nautobot_chatops/migrations/0004_remove_params_rename_params1.py index 6d9d36a8..f8e2d17e 100644 --- a/nautobot_chatops/migrations/0004_remove_params_rename_params1.py +++ b/nautobot_chatops/migrations/0004_remove_params_rename_params1.py @@ -1,4 +1,4 @@ -from django.db import migrations, connection +from django.db import connection, migrations class Migration(migrations.Migration): diff --git a/nautobot_chatops/migrations/0005_grafana.py b/nautobot_chatops/migrations/0005_grafana.py index 33d2994a..479203a4 100644 --- a/nautobot_chatops/migrations/0005_grafana.py +++ b/nautobot_chatops/migrations/0005_grafana.py @@ -1,13 +1,14 @@ # Generated by Django 3.2.15 on 2023-05-19 11:57 +import uuid + import django.core.serializers.json -from django.db import migrations, models import django.db.models.deletion import nautobot.extras.models.mixins import taggit.managers -import uuid -from django.db.migrations.recorder import MigrationRecorder from django.contrib.contenttypes.models import ContentType +from django.db import migrations, models +from django.db.migrations.recorder import MigrationRecorder _APP_LABEL = "nautobot_chatops" _OLD_APP_LABEL = "nautobot_plugin_chatops_grafana" @@ -22,7 +23,7 @@ def _copy(model_name, apps, connection): with connection.cursor() as cursor: # Table names are from trusted source (this script) - cursor.execute(f"INSERT INTO {new_table_name} SELECT * FROM {old_table_name};") # nosec + cursor.execute(f"INSERT INTO {new_table_name} SELECT * FROM {old_table_name};") # noqa: S608 # Update the content type to point to the new model old_content_type = ContentType.objects.get(app_label=_OLD_APP_LABEL, model=model_name.lower()) diff --git a/nautobot_chatops/models.py b/nautobot_chatops/models.py index 49c7e207..810be615 100644 --- a/nautobot_chatops/models.py +++ b/nautobot_chatops/models.py @@ -2,31 +2,28 @@ from django.core.exceptions import ValidationError from django.db import models - -from nautobot.utilities.fields import ColorField -from nautobot.extras.models.change_logging import ChangeLoggedModel from nautobot.core.models import BaseModel +from nautobot.extras.models.change_logging import ChangeLoggedModel +from nautobot.utilities.fields import ColorField from .choices import AccessGrantTypeChoices, CommandStatusChoices, CommandTokenPlatformChoices - -from .integrations.grafana.models import Dashboard as GrafanaDashboard -from .integrations.grafana.models import Panel as GrafanaPanel -from .integrations.grafana.models import PanelVariable as GrafanaPanelVariable - from .constants import ( - COMMAND_LOG_USER_NAME_HELP_TEXT, - COMMAND_LOG_USER_ID_HELP_TEXT, - COMMAND_LOG_PLATFORM_HELP_TEXT, - COMMAND_LOG_COMMAND_TEXT, - COMMAND_LOG_SUBCOMMAND_HELP_TEXT, - COMMAND_LOG_PARAMS_HELP_TEXT, ACCESS_GRANT_COMMAND_HELP_TEXT, - ACCESS_GRANT_SUBCOMMAND_HELP_TEXT, ACCESS_GRANT_NAME_HELP_TEXT, + ACCESS_GRANT_SUBCOMMAND_HELP_TEXT, ACCESS_GRANT_VALUE_HELP_TEXT, + COMMAND_LOG_COMMAND_TEXT, + COMMAND_LOG_PARAMS_HELP_TEXT, + COMMAND_LOG_PLATFORM_HELP_TEXT, + COMMAND_LOG_SUBCOMMAND_HELP_TEXT, + COMMAND_LOG_USER_ID_HELP_TEXT, + COMMAND_LOG_USER_NAME_HELP_TEXT, COMMAND_TOKEN_COMMENT_HELP_TEXT, COMMAND_TOKEN_TOKEN_HELP_TEXT, ) +from .integrations.grafana.models import Dashboard as GrafanaDashboard +from .integrations.grafana.models import Panel as GrafanaPanel +from .integrations.grafana.models import PanelVariable as GrafanaPanelVariable class CommandLog(BaseModel): diff --git a/nautobot_chatops/navigation.py b/nautobot_chatops/navigation.py index 27c28a7e..9b535017 100644 --- a/nautobot_chatops/navigation.py +++ b/nautobot_chatops/navigation.py @@ -1,7 +1,7 @@ """Plugin additions to the Nautobot navigation menu.""" from django.conf import settings -from nautobot.extras.plugins import PluginMenuItem, PluginMenuButton +from nautobot.extras.plugins import PluginMenuButton, PluginMenuItem from nautobot.utilities.choices import ButtonColorChoices if settings.PLUGINS_CONFIG["nautobot_chatops"]["enable_grafana"]: diff --git a/nautobot_chatops/sockets/__init__.py b/nautobot_chatops/sockets/__init__.py index e69de29b..c63edf0d 100644 --- a/nautobot_chatops/sockets/__init__.py +++ b/nautobot_chatops/sockets/__init__.py @@ -0,0 +1 @@ +"""Nautobot ChatOps Sockets.""" diff --git a/nautobot_chatops/sockets/slack.py b/nautobot_chatops/sockets/slack.py index bf1f6c22..56c7cfa1 100644 --- a/nautobot_chatops/sockets/slack.py +++ b/nautobot_chatops/sockets/slack.py @@ -6,13 +6,13 @@ from django.conf import settings from slack_sdk.socket_mode.aiohttp import SocketModeClient -from slack_sdk.socket_mode.response import SocketModeResponse from slack_sdk.socket_mode.request import SocketModeRequest +from slack_sdk.socket_mode.response import SocketModeResponse from slack_sdk.web.async_client import AsyncWebClient -from nautobot_chatops.workers import get_commands_registry, commands_help, parse_command_string from nautobot_chatops.dispatchers.slack import SlackDispatcher from nautobot_chatops.utils import socket_check_and_enqueue_command +from nautobot_chatops.workers import commands_help, get_commands_registry, parse_command_string async def main(): # pylint: disable=too-many-statements diff --git a/nautobot_chatops/tables.py b/nautobot_chatops/tables.py index ca081dd0..39f89d83 100644 --- a/nautobot_chatops/tables.py +++ b/nautobot_chatops/tables.py @@ -1,10 +1,9 @@ """Django table classes for Nautobot.""" from django_tables2 import TemplateColumn - from nautobot.utilities.tables import BaseTable, ToggleColumn -from .models import CommandLog, AccessGrant, CommandToken +from .models import AccessGrant, CommandLog, CommandToken class CommandLogTable(BaseTable): diff --git a/nautobot_chatops/tests/aci/test_aci.py b/nautobot_chatops/tests/aci/test_aci.py index 2126f813..14519e3b 100644 --- a/nautobot_chatops/tests/aci/test_aci.py +++ b/nautobot_chatops/tests/aci/test_aci.py @@ -1,7 +1,9 @@ """Tests for integrations.aci.aci.""" + # pylint: disable=no-self-use, import-outside-toplevel, invalid-name import unittest -from unittest.mock import patch, Mock +from unittest.mock import Mock, patch + from nautobot_chatops.integrations.aci.aci import NautobotPluginChatopsAci, RequestHTTPError @@ -312,9 +314,7 @@ def test_get_static_path_negative(self, mocked_login, mocked_handle_request): mock_response.ok = False mocked_login.return_value = self.mock_login mocked_handle_request.return_value = mock_response - self.assertRaises( - RequestHTTPError, self.aci_obj.get_static_path, "test-tenant-1", "test-ap", "test-epg" - ) # nosec + self.assertRaises(RequestHTTPError, self.aci_obj.get_static_path, "test-tenant-1", "test-ap", "test-epg") # nosec @patch.object(NautobotPluginChatopsAci, "get_static_path") @patch.object(NautobotPluginChatopsAci, "get_contract_filters") diff --git a/nautobot_chatops/tests/ansible/test_tower.py b/nautobot_chatops/tests/ansible/test_tower.py index f8c28f0a..a28aeeb9 100644 --- a/nautobot_chatops/tests/ansible/test_tower.py +++ b/nautobot_chatops/tests/ansible/test_tower.py @@ -1,4 +1,5 @@ """Test of tower.py.""" + from collections import namedtuple from os import path diff --git a/nautobot_chatops/tests/aristacv/test_api.py b/nautobot_chatops/tests/aristacv/test_api.py index a09083d2..4b58e0c2 100644 --- a/nautobot_chatops/tests/aristacv/test_api.py +++ b/nautobot_chatops/tests/aristacv/test_api.py @@ -1,12 +1,12 @@ """Unit tests for Arista CloudVision integration API.""" + from django.contrib.auth import get_user_model from django.test import TestCase from django.urls import reverse +from nautobot.users.models import Token from rest_framework import status from rest_framework.test import APIClient -from nautobot.users.models import Token - User = get_user_model() diff --git a/nautobot_chatops/tests/grafana/test_api.py b/nautobot_chatops/tests/grafana/test_api.py index 759571a0..4a5af59c 100644 --- a/nautobot_chatops/tests/grafana/test_api.py +++ b/nautobot_chatops/tests/grafana/test_api.py @@ -1,12 +1,12 @@ """Unit tests for nautobot_chatops.integrations.grafana.""" + from django.contrib.auth import get_user_model from django.test import TestCase from django.urls import reverse +from nautobot.users.models import Token from rest_framework import status from rest_framework.test import APIClient -from nautobot.users.models import Token - User = get_user_model() diff --git a/nautobot_chatops/tests/grafana/test_workers.py b/nautobot_chatops/tests/grafana/test_workers.py index 36f5f7a7..2e9e214d 100644 --- a/nautobot_chatops/tests/grafana/test_workers.py +++ b/nautobot_chatops/tests/grafana/test_workers.py @@ -1,13 +1,13 @@ """Test cases for the Nautobot workers module.""" -from django.test import TestCase +from django.test import TestCase from prybar import dynamic_entrypoint -from nautobot_chatops.workers import parse_command_string, get_commands_registry, add_subcommand -from nautobot_chatops.tests.workers.dynamic_commands import dynamic_command, dynamic_subcommand import nautobot_chatops.workers +from nautobot_chatops.integrations.grafana.models import Dashboard, Panel from nautobot_chatops.integrations.grafana.worker import initialize_subcommands -from nautobot_chatops.integrations.grafana.models import Panel, Dashboard +from nautobot_chatops.tests.workers.dynamic_commands import dynamic_command, dynamic_subcommand +from nautobot_chatops.workers import add_subcommand, get_commands_registry, parse_command_string class TestGrafana(TestCase): diff --git a/nautobot_chatops/tests/ipfabric/test_api.py b/nautobot_chatops/tests/ipfabric/test_api.py index d6638d9d..ca5b63ad 100644 --- a/nautobot_chatops/tests/ipfabric/test_api.py +++ b/nautobot_chatops/tests/ipfabric/test_api.py @@ -1,12 +1,12 @@ """Unit tests for IP Fabric Integration.""" + from django.contrib.auth import get_user_model from django.test import TestCase from django.urls import reverse +from nautobot.users.models import Token from rest_framework import status from rest_framework.test import APIClient -from nautobot.users.models import Token - User = get_user_model() diff --git a/nautobot_chatops/tests/meraki/test_utils.py b/nautobot_chatops/tests/meraki/test_utils.py index 31680cdd..d851135d 100644 --- a/nautobot_chatops/tests/meraki/test_utils.py +++ b/nautobot_chatops/tests/meraki/test_utils.py @@ -1,4 +1,5 @@ """Test of utils.py.""" + import unittest from unittest.mock import patch diff --git a/nautobot_chatops/tests/panorama/test_utils_panorama.py b/nautobot_chatops/tests/panorama/test_utils_panorama.py index 259583b0..fc922415 100644 --- a/nautobot_chatops/tests/panorama/test_utils_panorama.py +++ b/nautobot_chatops/tests/panorama/test_utils_panorama.py @@ -1,6 +1,9 @@ """Unit tests for Panorama utility functions.""" + from unittest.mock import MagicMock + from nautobot.utilities.testing import TestCase + from nautobot_chatops.integrations.panorama.utils import get_from_pano diff --git a/nautobot_chatops/tests/test_api.py b/nautobot_chatops/tests/test_api.py index 09a7cce5..3608a18f 100644 --- a/nautobot_chatops/tests/test_api.py +++ b/nautobot_chatops/tests/test_api.py @@ -1,4 +1,5 @@ """Test cases for Nautobot Chatops API.""" + try: from importlib import metadata except ImportError: @@ -6,10 +7,9 @@ import importlib_metadata as metadata from django.urls import reverse - from nautobot.utilities.testing import APITestCase, APIViewTestCases -from nautobot_chatops.models import AccessGrant, CommandToken +from nautobot_chatops.models import AccessGrant, CommandToken nautobot_version = metadata.version("nautobot") diff --git a/nautobot_chatops/tests/test_dispatchers.py b/nautobot_chatops/tests/test_dispatchers.py index efea9bfb..d4c032ea 100644 --- a/nautobot_chatops/tests/test_dispatchers.py +++ b/nautobot_chatops/tests/test_dispatchers.py @@ -1,15 +1,15 @@ """Tests for Nautobot dispatcher class implementations.""" -from unittest.mock import patch, MagicMock + +from unittest.mock import MagicMock, patch from django.conf import settings from django.test import TestCase from slack_sdk.errors import SlackApiError +from nautobot_chatops.dispatchers.mattermost import MattermostDispatcher from nautobot_chatops.dispatchers.ms_teams import MSTeamsDispatcher from nautobot_chatops.dispatchers.slack import SlackDispatcher from nautobot_chatops.dispatchers.webex import WebexDispatcher -from nautobot_chatops.dispatchers.mattermost import MattermostDispatcher - # pylint: disable=unnecessary-pass diff --git a/nautobot_chatops/tests/test_models.py b/nautobot_chatops/tests/test_models.py index 44e6aded..b70e8ba6 100644 --- a/nautobot_chatops/tests/test_models.py +++ b/nautobot_chatops/tests/test_models.py @@ -1,8 +1,10 @@ """Tests for ChatOps models.""" + import datetime + from django.test import TestCase -from nautobot_chatops.choices import CommandStatusChoices +from nautobot_chatops.choices import CommandStatusChoices from nautobot_chatops.models import CommandLog diff --git a/nautobot_chatops/tests/test_views.py b/nautobot_chatops/tests/test_views.py index e48120e1..121cef94 100644 --- a/nautobot_chatops/tests/test_views.py +++ b/nautobot_chatops/tests/test_views.py @@ -5,21 +5,23 @@ from django.conf import settings from django.test import TestCase, override_settings from django.test.client import RequestFactory - from nautobot.utilities.testing import ViewTestCases + +from nautobot_chatops.api.views.mattermost import verify_signature as mattermost_verify_signature from nautobot_chatops.api.views.slack import ( - verify_signature as slack_verify_signature, generate_signature as slack_generate_signature, ) +from nautobot_chatops.api.views.slack import ( + verify_signature as slack_verify_signature, +) from nautobot_chatops.api.views.webex import ( - verify_signature as webex_verify_signature, generate_signature as webex_generate_signature, ) -from nautobot_chatops.api.views.mattermost import verify_signature as mattermost_verify_signature -from nautobot_chatops.choices import CommandTokenPlatformChoices -from nautobot_chatops.models import CommandToken -from nautobot_chatops.choices import CommandStatusChoices -from nautobot_chatops.models import CommandLog +from nautobot_chatops.api.views.webex import ( + verify_signature as webex_verify_signature, +) +from nautobot_chatops.choices import CommandStatusChoices, CommandTokenPlatformChoices +from nautobot_chatops.models import CommandLog, CommandToken class TestSignatureVerification(TestCase): diff --git a/nautobot_chatops/tests/test_workers.py b/nautobot_chatops/tests/test_workers.py index 65120bfb..56bae1f7 100644 --- a/nautobot_chatops/tests/test_workers.py +++ b/nautobot_chatops/tests/test_workers.py @@ -1,11 +1,11 @@ """Test cases for the Nautobot workers module.""" -from django.test import SimpleTestCase +from django.test import SimpleTestCase from prybar import dynamic_entrypoint -from nautobot_chatops.workers import convert_smart_quotes, parse_command_string, get_commands_registry, add_subcommand -from nautobot_chatops.tests.workers.dynamic_commands import dynamic_command, dynamic_subcommand import nautobot_chatops.workers +from nautobot_chatops.tests.workers.dynamic_commands import dynamic_command, dynamic_subcommand +from nautobot_chatops.workers import add_subcommand, convert_smart_quotes, get_commands_registry, parse_command_string class TestFunctions(SimpleTestCase): @@ -128,11 +128,11 @@ def test_convert_smart_quotes(self): """Verify Convert Smart Quotes.""" self.assertEqual(convert_smart_quotes("''"), "''") self.assertEqual(convert_smart_quotes(""), "") - self.assertEqual(convert_smart_quotes("\u201C\u201D"), "''") + self.assertEqual(convert_smart_quotes("\u201c\u201d"), "''") self.assertEqual(convert_smart_quotes("\u2018\u2019"), "''") - self.assertEqual(convert_smart_quotes("\u201C"), "'") - self.assertEqual(convert_smart_quotes("\u201D"), "'") + self.assertEqual(convert_smart_quotes("\u201c"), "'") + self.assertEqual(convert_smart_quotes("\u201d"), "'") self.assertEqual(convert_smart_quotes("\u2018"), "'") self.assertEqual(convert_smart_quotes("\u2019"), "'") - self.assertEqual(convert_smart_quotes("\u201CLas Vegas\u201D"), "'Las Vegas'") + self.assertEqual(convert_smart_quotes("\u201cLas Vegas\u201d"), "'Las Vegas'") self.assertEqual(convert_smart_quotes("\u2018Las Vegas\u2019"), "'Las Vegas'") diff --git a/nautobot_chatops/tests/workers/dynamic_commands.py b/nautobot_chatops/tests/workers/dynamic_commands.py index b6b3dbcc..ac8c0f82 100644 --- a/nautobot_chatops/tests/workers/dynamic_commands.py +++ b/nautobot_chatops/tests/workers/dynamic_commands.py @@ -1,7 +1,8 @@ """Test file defining a dynamic subcommand (issue #54).""" from django_rq import job -from nautobot_chatops.workers import subcommand_of, handle_subcommands + +from nautobot_chatops.workers import handle_subcommands, subcommand_of @job("default") diff --git a/nautobot_chatops/tests/workers/test_nautobot.py b/nautobot_chatops/tests/workers/test_nautobot.py index a6c7db59..0b512bc8 100644 --- a/nautobot_chatops/tests/workers/test_nautobot.py +++ b/nautobot_chatops/tests/workers/test_nautobot.py @@ -1,12 +1,13 @@ """Tests for the /nautobot chatops commands.""" + from unittest.mock import MagicMock from django.test import TestCase from nautobot.dcim.models import Site -from nautobot.ipam.models import VLAN from nautobot.extras.models import Status -from nautobot_chatops.choices import CommandStatusChoices +from nautobot.ipam.models import VLAN +from nautobot_chatops.choices import CommandStatusChoices from nautobot_chatops.dispatchers import Dispatcher from nautobot_chatops.workers.nautobot import get_vlans diff --git a/nautobot_chatops/tests/workers/two_commands.py b/nautobot_chatops/tests/workers/two_commands.py index acf397ee..e6db6c86 100644 --- a/nautobot_chatops/tests/workers/two_commands.py +++ b/nautobot_chatops/tests/workers/two_commands.py @@ -1,7 +1,8 @@ """Test file defining two commands and their subcommands (issue #20).""" from django_rq import job -from nautobot_chatops.workers import subcommand_of, handle_subcommands + +from nautobot_chatops.workers import handle_subcommands, subcommand_of @job("default") diff --git a/nautobot_chatops/urls.py b/nautobot_chatops/urls.py index 17b06bf6..b1130c42 100644 --- a/nautobot_chatops/urls.py +++ b/nautobot_chatops/urls.py @@ -1,22 +1,22 @@ """Django urlpatterns declaration for nautobot_chatops plugin.""" + import logging from django.conf import settings from django.urls import path - from nautobot.extras.views import ObjectChangeLogView from nautobot_chatops.models import AccessGrant, CommandToken from nautobot_chatops.views import ( + AccessGrantBulkDeleteView, + AccessGrantCreateView, + AccessGrantListView, + AccessGrantView, CommandTokenBulkDeleteView, CommandTokenCreateView, CommandTokenListView, CommandTokenView, NautobotHomeView, - AccessGrantListView, - AccessGrantView, - AccessGrantCreateView, - AccessGrantBulkDeleteView, ) if settings.PLUGINS_CONFIG["nautobot_chatops"]["enable_grafana"]: diff --git a/nautobot_chatops/utils.py b/nautobot_chatops/utils.py index 3a3c9df1..df125655 100644 --- a/nautobot_chatops/utils.py +++ b/nautobot_chatops/utils.py @@ -1,8 +1,8 @@ """Utility functions for API view implementations.""" -from datetime import datetime, timezone import logging import sys +from datetime import datetime, timezone from asgiref.sync import sync_to_async from django.conf import settings @@ -10,8 +10,8 @@ from django.http import HttpResponse, JsonResponse from nautobot_chatops.choices import AccessGrantTypeChoices, CommandStatusChoices -from nautobot_chatops.models import AccessGrant, CommandLog from nautobot_chatops.metrics import request_command_cntr +from nautobot_chatops.models import AccessGrant, CommandLog logger = logging.getLogger(__name__) @@ -133,9 +133,7 @@ def socket_check_and_enqueue_command(*args, **kwargs): return check_and_enqueue_command(*args, **kwargs) -def check_and_enqueue_command( - registry, command, subcommand, params, context, dispatcher_class -): # pylint:disable=too-many-statements +def check_and_enqueue_command(registry, command, subcommand, params, context, dispatcher_class): # pylint:disable=too-many-statements """Check whether the given command is permitted by any access grants, and enqueue it if permitted. For a command/subcommand to be permitted, we must have: diff --git a/nautobot_chatops/views.py b/nautobot_chatops/views.py index 7fd95971..50e5e570 100644 --- a/nautobot_chatops/views.py +++ b/nautobot_chatops/views.py @@ -4,26 +4,25 @@ to send requests and notifications to. """ -from django.contrib.auth.mixins import PermissionRequiredMixin -from django.contrib.auth.decorators import login_required from django.contrib import messages -from django.shortcuts import render, redirect +from django.contrib.auth.decorators import login_required +from django.contrib.auth.mixins import PermissionRequiredMixin +from django.http import HttpResponse +from django.shortcuts import redirect, render +from django.template.defaulttags import register from django.utils.decorators import method_decorator from django.views import View -from django.template.defaulttags import register -from django.http import HttpResponse from django_tables2 import RequestConfig - -from nautobot.core.views.generic import ObjectListView, ObjectEditView, BulkDeleteView -from nautobot.utilities.paginator import EnhancedPaginator, get_paginate_count +from nautobot.core.views.generic import BulkDeleteView, ObjectEditView, ObjectListView from nautobot.utilities.forms import TableConfigForm +from nautobot.utilities.paginator import EnhancedPaginator, get_paginate_count from nautobot.utilities.utils import csv_format -from nautobot_chatops.workers import get_commands_registry -from nautobot_chatops.models import CommandLog, AccessGrant, CommandToken -from nautobot_chatops.filters import CommandLogFilterSet, AccessGrantFilterSet, CommandTokenFilterSet +from nautobot_chatops.filters import AccessGrantFilterSet, CommandLogFilterSet, CommandTokenFilterSet from nautobot_chatops.forms import AccessGrantFilterForm, AccessGrantForm, CommandTokenFilterForm, CommandTokenForm -from nautobot_chatops.tables import CommandLogTable, AccessGrantTable, CommandTokenTable +from nautobot_chatops.models import AccessGrant, CommandLog, CommandToken +from nautobot_chatops.tables import AccessGrantTable, CommandLogTable, CommandTokenTable +from nautobot_chatops.workers import get_commands_registry @register.filter diff --git a/nautobot_chatops/workers/__init__.py b/nautobot_chatops/workers/__init__.py index 2065090d..d0e1962a 100644 --- a/nautobot_chatops/workers/__init__.py +++ b/nautobot_chatops/workers/__init__.py @@ -6,21 +6,21 @@ back to the chat using the provided ``dispatchers`` instance's generic API. """ -from datetime import datetime, timezone import inspect -from functools import wraps import logging import shlex -import pkg_resources +from datetime import datetime, timezone +from functools import wraps +import pkg_resources from django.conf import settings from django.db.models import Q from nautobot_chatops.choices import AccessGrantTypeChoices, CommandStatusChoices from nautobot_chatops.integrations.utils import ALL_INTEGRATIONS, DISABLED_INTEGRATIONS +from nautobot_chatops.metrics import command_histogram, request_command_cntr from nautobot_chatops.models import AccessGrant from nautobot_chatops.utils import create_command_log -from nautobot_chatops.metrics import request_command_cntr, command_histogram logger = logging.getLogger(__name__) diff --git a/nautobot_chatops/workers/base.py b/nautobot_chatops/workers/base.py index f7add165..686838c9 100644 --- a/nautobot_chatops/workers/base.py +++ b/nautobot_chatops/workers/base.py @@ -2,12 +2,13 @@ Obsolete - contents of this file have been migrated to other modules. """ + import warnings # For backwards compatibility -- these used to be defined in this file # pylint: disable=unused-import -from nautobot_chatops.utils import create_command_log -from nautobot_chatops.workers import subcommand_of, handle_subcommands +from nautobot_chatops.utils import create_command_log # noqa: F401 +from nautobot_chatops.workers import subcommand_of, handle_subcommands # noqa: F401 warnings.warn( """Importing from `nautobot_chatops.workers.base` has been deprecated, please use `nautobot_chatops.workers` diff --git a/nautobot_chatops/workers/helper_functions.py b/nautobot_chatops/workers/helper_functions.py index 558d63a0..e8503443 100644 --- a/nautobot_chatops/workers/helper_functions.py +++ b/nautobot_chatops/workers/helper_functions.py @@ -1,6 +1,5 @@ """Helper functions for nautobot.py worker.""" - NAUTOBOT_LOGO_PATH = "nautobot/NautobotLogoSquare.png" NAUTOBOT_LOGO_ALT = "Nautobot Logo" diff --git a/nautobot_chatops/workers/nautobot.py b/nautobot_chatops/workers/nautobot.py index 642d457a..d2f465fb 100644 --- a/nautobot_chatops/workers/nautobot.py +++ b/nautobot_chatops/workers/nautobot.py @@ -1,25 +1,24 @@ """Worker functions for interacting with Nautobot.""" +from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ValidationError from django.db.models import Count -from django.contrib.contenttypes.models import ContentType from django_rq import job - -from nautobot.dcim.models.device_components import Interface, FrontPort, RearPort -from nautobot.circuits.models import Circuit, CircuitType, Provider, CircuitTermination +from nautobot.circuits.models import Circuit, CircuitTermination, CircuitType, Provider from nautobot.dcim.choices import DeviceStatusChoices -from nautobot.dcim.models import Device, Site, DeviceRole, DeviceType, Manufacturer, Rack, Region, Cable -from nautobot.ipam.models import VLAN, Prefix, VLANGroup, Role -from nautobot.tenancy.models import Tenant +from nautobot.dcim.models import Cable, Device, DeviceRole, DeviceType, Manufacturer, Rack, Region, Site +from nautobot.dcim.models.device_components import FrontPort, Interface, RearPort from nautobot.extras.models import Status +from nautobot.ipam.models import VLAN, Prefix, Role, VLANGroup +from nautobot.tenancy.models import Tenant from nautobot_chatops.choices import CommandStatusChoices -from nautobot_chatops.workers import subcommand_of, handle_subcommands +from nautobot_chatops.workers import handle_subcommands, subcommand_of from nautobot_chatops.workers.helper_functions import ( add_asterisk, + menu_item_check, menu_offset_value, nautobot_logo, - menu_item_check, prompt_for_circuit_filter_type, prompt_for_device_filter_type, prompt_for_interface_filter_type, @@ -123,11 +122,7 @@ def get_filtered_connections(device, interface_ct): status__slug="connected", termination_a_type=interface_ct.pk, termination_b_type=interface_ct.pk, - ).exclude( - _termination_b_device=None - ).exclude( - _termination_a_device=None - ) + ).exclude(_termination_b_device=None).exclude(_termination_a_device=None) def analyze_circuit_endpoints(endpoint): diff --git a/poetry.lock b/poetry.lock index ae795cb5..78b07fda 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,10 +1,9 @@ -# This file is automatically @generated by Poetry 1.4.0 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "aiodns" version = "1.2.0" description = "Simple DNS resolver for asyncio" -category = "main" optional = false python-versions = "*" files = [ @@ -20,7 +19,6 @@ typing = "*" name = "aiohttp" version = "3.8.5" description = "Async http client/server framework (asyncio)" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -129,7 +127,6 @@ speedups = ["Brotli", "aiodns", "cchardet"] name = "aiosignal" version = "1.3.1" description = "aiosignal: a list of registered asynchronous callbacks" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -144,7 +141,6 @@ frozenlist = ">=1.1.0" name = "amqp" version = "5.1.1" description = "Low-level AMQP client for Python (fork of amqplib)." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -159,7 +155,6 @@ vine = ">=5.0.0" name = "aniso8601" version = "7.0.0" description = "A library for parsing ISO 8601 strings." -category = "main" optional = false python-versions = "*" files = [ @@ -171,7 +166,6 @@ files = [ name = "anyio" version = "4.0.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -193,7 +187,6 @@ trio = ["trio (>=0.22)"] name = "arrow" version = "1.2.3" description = "Better dates & times for Python" -category = "main" optional = true python-versions = ">=3.6" files = [ @@ -208,7 +201,6 @@ python-dateutil = ">=2.7.0" name = "asgiref" version = "3.7.2" description = "ASGI specs, helper code, and adapters" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -226,7 +218,6 @@ tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] name = "astroid" version = "2.11.7" description = "An abstract syntax tree for Python with inference support." -category = "dev" optional = false python-versions = ">=3.6.2" files = [ @@ -244,7 +235,6 @@ wrapt = ">=1.11,<2" name = "async-timeout" version = "4.0.3" description = "Timeout context manager for asyncio programs" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -256,7 +246,6 @@ files = [ name = "attrs" version = "23.1.0" description = "Classes Without Boilerplate" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -275,7 +264,6 @@ tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pyte name = "backports-zoneinfo" version = "0.2.1" description = "Backport of the standard library zoneinfo module" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -303,35 +291,10 @@ tzdata = {version = "*", optional = true, markers = "extra == \"tzdata\""} [package.extras] tzdata = ["tzdata"] -[[package]] -name = "bandit" -version = "1.7.5" -description = "Security oriented static analyser for python code." -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "bandit-1.7.5-py3-none-any.whl", hash = "sha256:75665181dc1e0096369112541a056c59d1c5f66f9bb74a8d686c3c362b83f549"}, - {file = "bandit-1.7.5.tar.gz", hash = "sha256:bdfc739baa03b880c2d15d0431b31c658ffc348e907fe197e54e0389dd59e11e"}, -] - -[package.dependencies] -colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} -GitPython = ">=1.0.1" -PyYAML = ">=5.3.1" -rich = "*" -stevedore = ">=1.20.0" - -[package.extras] -test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)", "tomli (>=1.1.0)"] -toml = ["tomli (>=1.1.0)"] -yaml = ["PyYAML"] - [[package]] name = "bcrypt" version = "4.0.1" description = "Modern password hashing for your software and your servers" -category = "main" optional = true python-versions = ">=3.6" files = [ @@ -366,7 +329,6 @@ typecheck = ["mypy"] name = "billiard" version = "4.1.0" description = "Python multiprocessing fork with improvements and bugfixes" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -374,58 +336,10 @@ files = [ {file = "billiard-4.1.0.tar.gz", hash = "sha256:1ad2eeae8e28053d729ba3373d34d9d6e210f6e4d8bf0a9c64f92bd053f1edf5"}, ] -[[package]] -name = "black" -version = "23.9.1" -description = "The uncompromising code formatter." -category = "dev" -optional = false -python-versions = ">=3.8" -files = [ - {file = "black-23.9.1-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:d6bc09188020c9ac2555a498949401ab35bb6bf76d4e0f8ee251694664df6301"}, - {file = "black-23.9.1-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:13ef033794029b85dfea8032c9d3b92b42b526f1ff4bf13b2182ce4e917f5100"}, - {file = "black-23.9.1-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:75a2dc41b183d4872d3a500d2b9c9016e67ed95738a3624f4751a0cb4818fe71"}, - {file = "black-23.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13a2e4a93bb8ca74a749b6974925c27219bb3df4d42fc45e948a5d9feb5122b7"}, - {file = "black-23.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:adc3e4442eef57f99b5590b245a328aad19c99552e0bdc7f0b04db6656debd80"}, - {file = "black-23.9.1-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:8431445bf62d2a914b541da7ab3e2b4f3bc052d2ccbf157ebad18ea126efb91f"}, - {file = "black-23.9.1-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:8fc1ddcf83f996247505db6b715294eba56ea9372e107fd54963c7553f2b6dfe"}, - {file = "black-23.9.1-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:7d30ec46de88091e4316b17ae58bbbfc12b2de05e069030f6b747dfc649ad186"}, - {file = "black-23.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:031e8c69f3d3b09e1aa471a926a1eeb0b9071f80b17689a655f7885ac9325a6f"}, - {file = "black-23.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:538efb451cd50f43aba394e9ec7ad55a37598faae3348d723b59ea8e91616300"}, - {file = "black-23.9.1-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:638619a559280de0c2aa4d76f504891c9860bb8fa214267358f0a20f27c12948"}, - {file = "black-23.9.1-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:a732b82747235e0542c03bf352c126052c0fbc458d8a239a94701175b17d4855"}, - {file = "black-23.9.1-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:cf3a4d00e4cdb6734b64bf23cd4341421e8953615cba6b3670453737a72ec204"}, - {file = "black-23.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf99f3de8b3273a8317681d8194ea222f10e0133a24a7548c73ce44ea1679377"}, - {file = "black-23.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:14f04c990259576acd093871e7e9b14918eb28f1866f91968ff5524293f9c573"}, - {file = "black-23.9.1-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:c619f063c2d68f19b2d7270f4cf3192cb81c9ec5bc5ba02df91471d0b88c4c5c"}, - {file = "black-23.9.1-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:6a3b50e4b93f43b34a9d3ef00d9b6728b4a722c997c99ab09102fd5efdb88325"}, - {file = "black-23.9.1-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:c46767e8df1b7beefb0899c4a95fb43058fa8500b6db144f4ff3ca38eb2f6393"}, - {file = "black-23.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50254ebfa56aa46a9fdd5d651f9637485068a1adf42270148cd101cdf56e0ad9"}, - {file = "black-23.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:403397c033adbc45c2bd41747da1f7fc7eaa44efbee256b53842470d4ac5a70f"}, - {file = "black-23.9.1-py3-none-any.whl", hash = "sha256:6ccd59584cc834b6d127628713e4b6b968e5f79572da66284532525a042549f9"}, - {file = "black-23.9.1.tar.gz", hash = "sha256:24b6b3ff5c6d9ea08a8888f6977eae858e1f340d7260cf56d70a49823236b62d"}, -] - -[package.dependencies] -click = ">=8.0.0" -mypy-extensions = ">=0.4.3" -packaging = ">=22.0" -pathspec = ">=0.9.0" -platformdirs = ">=2" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)"] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] - [[package]] name = "celery" version = "5.3.4" description = "Distributed Task Queue." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -482,7 +396,6 @@ zstd = ["zstandard (==0.21.0)"] name = "certifi" version = "2023.7.22" description = "Python package for providing Mozilla's CA Bundle." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -494,7 +407,6 @@ files = [ name = "cffi" version = "1.16.0" description = "Foreign Function Interface for Python calling C code." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -559,7 +471,6 @@ pycparser = "*" name = "charset-normalizer" version = "3.2.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "main" optional = false python-versions = ">=3.7.0" files = [ @@ -644,7 +555,6 @@ files = [ name = "click" version = "8.1.7" description = "Composable command line interface toolkit" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -659,7 +569,6 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} name = "click-default-group" version = "1.2.4" description = "click_default_group" -category = "dev" optional = false python-versions = ">=2.7" files = [ @@ -677,7 +586,6 @@ test = ["pytest"] name = "click-didyoumean" version = "0.3.0" description = "Enables git-like *did-you-mean* feature in click" -category = "main" optional = false python-versions = ">=3.6.2,<4.0.0" files = [ @@ -692,7 +600,6 @@ click = ">=7" name = "click-plugins" version = "1.1.1" description = "An extension module for click to enable registering CLI commands via setuptools entry-points." -category = "main" optional = false python-versions = "*" files = [ @@ -710,7 +617,6 @@ dev = ["coveralls", "pytest (>=3.6)", "pytest-cov", "wheel"] name = "click-repl" version = "0.3.0" description = "REPL plugin for Click" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -729,7 +635,6 @@ testing = ["pytest (>=7.2.1)", "pytest-cov (>=4.0.0)", "tox (>=4.4.3)"] name = "cloudvision" version = "1.13.0" description = "A Python library for Arista's CloudVision APIs." -category = "main" optional = true python-versions = "*" files = [ @@ -752,7 +657,6 @@ typing-extensions = ">=4.2.0" name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ @@ -764,7 +668,6 @@ files = [ name = "coverage" version = "5.4" description = "Code coverage measurement for Python" -category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" files = [ @@ -826,7 +729,6 @@ toml = ["toml"] name = "cron-descriptor" version = "1.4.0" description = "A Python library that converts cron expressions into human readable strings." -category = "main" optional = false python-versions = "*" files = [ @@ -840,7 +742,6 @@ dev = ["polib"] name = "cryptography" version = "41.0.4" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -886,7 +787,6 @@ test-randomorder = ["pytest-randomly"] name = "cvprac" version = "1.3.1" description = "Arista Cloudvision(R) Portal Rest API Client written in python" -category = "main" optional = true python-versions = "*" files = [ @@ -903,7 +803,6 @@ dev = ["check-manifest", "coverage", "pep8", "pyflakes", "pylint", "pyyaml"] name = "deepdiff" version = "6.5.0" description = "Deep Difference and Search of any Python object/data. Recreate objects by adding adding deltas to each other." -category = "main" optional = true python-versions = ">=3.7" files = [ @@ -922,7 +821,6 @@ optimize = ["orjson"] name = "defusedxml" version = "0.7.1" description = "XML bomb protection for Python stdlib modules" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -934,7 +832,6 @@ files = [ name = "diffsync" version = "1.8.0" description = "Library to easily sync/diff/update 2 different data sources" -category = "main" optional = true python-versions = ">=3.7,<4.0" files = [ @@ -955,7 +852,6 @@ redis = ["redis (>=4.3,<5.0)"] name = "dill" version = "0.3.7" description = "serialize all of Python" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -970,7 +866,6 @@ graph = ["objgraph (>=1.7.2)"] name = "django" version = "3.2.21" description = "A high-level Python Web framework that encourages rapid development and clean, pragmatic design." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -991,7 +886,6 @@ bcrypt = ["bcrypt"] name = "django-ajax-tables" version = "1.1.1" description = "Django tag for ajax-enabled tables" -category = "main" optional = false python-versions = "*" files = [ @@ -1003,7 +897,6 @@ files = [ name = "django-appconf" version = "1.0.5" description = "A helper class for handling configuration defaults of packaged apps gracefully." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1018,7 +911,6 @@ django = "*" name = "django-cacheops" version = "6.2" description = "A slick ORM cache with automatic granular event-driven invalidation for Django." -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -1035,7 +927,6 @@ six = ">=1.4.0" name = "django-celery-beat" version = "2.5.0" description = "Database-backed Periodic Tasks." -category = "main" optional = false python-versions = "*" files = [ @@ -1056,7 +947,6 @@ tzdata = "*" name = "django-constance" version = "2.9.1" description = "Django live settings with pluggable backends, including Redis." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1075,7 +965,6 @@ redis = ["redis"] name = "django-cors-headers" version = "4.2.0" description = "django-cors-headers is a Django application for handling the server headers required for Cross-Origin Resource Sharing (CORS)." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1090,7 +979,6 @@ Django = ">=3.2" name = "django-cryptography" version = "1.1" description = "Easily encrypt data in Django" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1106,7 +994,6 @@ django-appconf = "*" name = "django-db-file-storage" version = "0.5.5" description = "Custom FILE_STORAGE for Django. Saves files in your database instead of your file system." -category = "main" optional = false python-versions = "*" files = [ @@ -1120,7 +1007,6 @@ Django = "*" name = "django-extensions" version = "3.2.3" description = "Extensions for Django" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1135,7 +1021,6 @@ Django = ">=3.2" name = "django-filter" version = "23.1" description = "Django-filter is a reusable Django application for allowing users to filter querysets dynamically." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1150,7 +1035,6 @@ Django = ">=3.2" name = "django-health-check" version = "3.17.0" description = "Run checks on services like databases, queue servers, celery processes, etc." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1169,7 +1053,6 @@ test = ["celery", "pytest", "pytest-cov", "pytest-django", "redis"] name = "django-jinja" version = "2.10.2" description = "Jinja2 templating language integrated in Django." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1185,7 +1068,6 @@ jinja2 = ">=3" name = "django-js-asset" version = "2.1.0" description = "script tag with additional attributes for django.forms.Media" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1203,7 +1085,6 @@ tests = ["coverage"] name = "django-mptt" version = "0.14.0" description = "Utilities for implementing Modified Preorder Tree Traversal with your Django Models and working with trees of Model instances." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1221,7 +1102,6 @@ tests = ["coverage", "mock-django"] name = "django-picklefield" version = "3.1" description = "Pickled object field for Django" -category = "main" optional = false python-versions = ">=3" files = [ @@ -1239,7 +1119,6 @@ tests = ["tox"] name = "django-prometheus" version = "2.3.1" description = "Django middlewares to monitor your application with Prometheus.io." -category = "main" optional = false python-versions = "*" files = [ @@ -1254,7 +1133,6 @@ prometheus-client = ">=0.7" name = "django-redis" version = "5.3.0" description = "Full featured redis cache backend for Django." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1273,7 +1151,6 @@ hiredis = ["redis[hiredis] (>=3,!=4.0.0,!=4.0.1)"] name = "django-rq" version = "2.8.1" description = "An app that provides django integration for RQ (Redis Queue)" -category = "main" optional = false python-versions = "*" files = [ @@ -1294,7 +1171,6 @@ testing = ["mock (>=2.0.0)"] name = "django-tables2" version = "2.6.0" description = "Table/data-grid framework for Django" -category = "main" optional = false python-versions = "*" files = [ @@ -1312,7 +1188,6 @@ tablib = ["tablib"] name = "django-taggit" version = "4.0.0" description = "django-taggit is a reusable Django application for simple tagging." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1327,7 +1202,6 @@ Django = ">=3.2" name = "django-timezone-field" version = "5.1" description = "A Django app providing DB, form, and REST framework fields for zoneinfo and pytz timezone objects." -category = "main" optional = false python-versions = ">=3.7,<4.0" files = [ @@ -1337,14 +1211,13 @@ files = [ [package.dependencies] "backports.zoneinfo" = {version = ">=0.2.1,<0.3.0", markers = "python_version < \"3.9\""} -Django = ">=2.2,<3.0.0 || >=3.2.0,<5.0" +Django = ">=2.2,<3.0.dev0 || >=3.2.dev0,<5.0" pytz = "*" [[package]] name = "django-tree-queries" version = "0.15.0" description = "Tree queries with explicit opt-in, without configurability" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1359,7 +1232,6 @@ tests = ["coverage"] name = "django-webserver" version = "1.2.0" description = "Django management commands for production webservers" -category = "main" optional = false python-versions = "*" files = [ @@ -1381,7 +1253,6 @@ waitress = ["waitress"] name = "djangorestframework" version = "3.14.0" description = "Web APIs for Django, made easy." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1397,7 +1268,6 @@ pytz = "*" name = "drf-spectacular" version = "0.26.5" description = "Sane and flexible OpenAPI 3 schema generation for Django REST framework" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1422,7 +1292,6 @@ sidecar = ["drf-spectacular-sidecar"] name = "drf-spectacular-sidecar" version = "2023.9.1" description = "Serve self-contained distribution builds of Swagger UI and Redoc with Django" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1437,7 +1306,6 @@ Django = ">=2.2" name = "drf-yasg" version = "1.21.7" description = "Automated generation of real Swagger/OpenAPI 2.0 schemas from Django Rest Framework code." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1463,7 +1331,6 @@ validation = ["swagger-spec-validator (>=2.1.0)"] name = "exceptiongroup" version = "1.1.3" description = "Backport of PEP 654 (exception groups)" -category = "main" optional = true python-versions = ">=3.7" files = [ @@ -1474,28 +1341,10 @@ files = [ [package.extras] test = ["pytest (>=6)"] -[[package]] -name = "flake8" -version = "3.9.2" -description = "the modular source code checker: pep8 pyflakes and co" -category = "dev" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" -files = [ - {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, - {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, -] - -[package.dependencies] -mccabe = ">=0.6.0,<0.7.0" -pycodestyle = ">=2.7.0,<2.8.0" -pyflakes = ">=2.3.0,<2.4.0" - [[package]] name = "fqdn" version = "1.5.1" description = "Validates fully-qualified domain names against RFC 1123, so that they are acceptable to modern bowsers" -category = "main" optional = true python-versions = ">=2.7, !=3.0, !=3.1, !=3.2, !=3.3, !=3.4, <4" files = [ @@ -1507,7 +1356,6 @@ files = [ name = "frozenlist" version = "1.4.0" description = "A list-like structure which implements collections.abc.MutableSequence" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1578,7 +1426,6 @@ files = [ name = "funcy" version = "1.18" description = "A fancy and practical functional tools" -category = "main" optional = false python-versions = "*" files = [ @@ -1590,7 +1437,6 @@ files = [ name = "future" version = "0.18.3" description = "Clean single-source support for Python 3 and 2" -category = "main" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -1601,7 +1447,6 @@ files = [ name = "ghp-import" version = "2.1.0" description = "Copy your docs directly to the gh-pages branch." -category = "dev" optional = false python-versions = "*" files = [ @@ -1619,7 +1464,6 @@ dev = ["flake8", "markdown", "twine", "wheel"] name = "gitdb" version = "4.0.10" description = "Git Object Database" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1634,7 +1478,6 @@ smmap = ">=3.0.1,<6" name = "gitpython" version = "3.1.37" description = "GitPython is a Python library used to interact with Git repositories" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1652,7 +1495,6 @@ test = ["black", "coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mypy", "pre-commit" name = "graphene" version = "2.1.9" description = "GraphQL Framework for Python" -category = "main" optional = false python-versions = "*" files = [ @@ -1675,7 +1517,6 @@ test = ["coveralls", "fastdiff (==0.2.0)", "iso8601", "mock", "promise", "pytest name = "graphene-django" version = "2.16.0" description = "Graphene Django integration" -category = "main" optional = false python-versions = "*" files = [ @@ -1700,7 +1541,6 @@ test = ["coveralls", "django-filter (>=2)", "djangorestframework (>=3.6.3)", "mo name = "graphene-django-optimizer" version = "0.8.0" description = "Optimize database access inside graphene queries." -category = "main" optional = false python-versions = "*" files = [ @@ -1711,7 +1551,6 @@ files = [ name = "graphql-core" version = "2.3.2" description = "GraphQL implementation for Python" -category = "main" optional = false python-versions = "*" files = [ @@ -1732,7 +1571,6 @@ test = ["coveralls (==1.11.1)", "cython (==0.29.17)", "gevent (==1.5.0)", "pyann name = "graphql-relay" version = "2.0.1" description = "Relay implementation for Python" -category = "main" optional = false python-versions = "*" files = [ @@ -1749,7 +1587,6 @@ six = ">=1.12" name = "griffe" version = "0.30.1" description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1764,7 +1601,6 @@ colorama = ">=0.4" name = "grpcio" version = "1.58.0" description = "HTTP/2-based RPC framework" -category = "main" optional = true python-versions = ">=3.7" files = [ @@ -1822,7 +1658,6 @@ protobuf = ["grpcio-tools (>=1.58.0)"] name = "h11" version = "0.14.0" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -category = "main" optional = true python-versions = ">=3.7" files = [ @@ -1834,7 +1669,6 @@ files = [ name = "httpcore" version = "0.16.3" description = "A minimal low-level HTTP client." -category = "main" optional = true python-versions = ">=3.7" files = [ @@ -1846,17 +1680,16 @@ files = [ anyio = ">=3.0,<5.0" certifi = "*" h11 = ">=0.13,<0.15" -sniffio = ">=1.0.0,<2.0.0" +sniffio = "==1.*" [package.extras] http2 = ["h2 (>=3,<5)"] -socks = ["socksio (>=1.0.0,<2.0.0)"] +socks = ["socksio (==1.*)"] [[package]] name = "httpx" version = "0.23.3" description = "The next generation HTTP client." -category = "main" optional = true python-versions = ">=3.7" files = [ @@ -1872,15 +1705,14 @@ sniffio = "*" [package.extras] brotli = ["brotli", "brotlicffi"] -cli = ["click (>=8.0.0,<9.0.0)", "pygments (>=2.0.0,<3.0.0)", "rich (>=10,<13)"] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<13)"] http2 = ["h2 (>=3,<5)"] -socks = ["socksio (>=1.0.0,<2.0.0)"] +socks = ["socksio (==1.*)"] [[package]] name = "idna" version = "3.4" description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -1892,7 +1724,6 @@ files = [ name = "importlib-metadata" version = "6.8.0" description = "Read metadata from Python packages" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1912,7 +1743,6 @@ testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs name = "importlib-resources" version = "6.1.0" description = "Read resources from Python packages" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1931,7 +1761,6 @@ testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", name = "incremental" version = "22.10.0" description = "\"A small library that versions your Python projects.\"" -category = "dev" optional = false python-versions = "*" files = [ @@ -1947,7 +1776,6 @@ scripts = ["click (>=6.0)", "twisted (>=16.4.0)"] name = "inflection" version = "0.5.1" description = "A port of Ruby on Rails inflector to Python" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -1959,7 +1787,6 @@ files = [ name = "invoke" version = "2.2.0" description = "Pythonic task execution" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1971,7 +1798,6 @@ files = [ name = "ipaddr" version = "2.2.0" description = "Google's IP address manipulation library" -category = "main" optional = true python-versions = "*" files = [ @@ -1982,7 +1808,6 @@ files = [ name = "ipfabric" version = "6.0.10" description = "Python package for interacting with IP Fabric" -category = "main" optional = true python-versions = ">=3.7.1,<4.0.0" files = [ @@ -2007,7 +1832,6 @@ examples = ["openpyxl (>=3.0.9,<4.0.0)", "pandas (>=1.3.0,<2.0.0)", "python-json name = "ipfabric-diagrams" version = "6.0.2" description = "Python package for interacting with IP Fabric Diagrams" -category = "main" optional = true python-versions = ">=3.7.1,<4.0.0" files = [ @@ -2027,7 +1851,6 @@ examples = ["rich (>=12.5.1,<13.0.0)"] name = "ipfabric-httpx-auth" version = "6.0.1" description = "DEPRECATED: Authentication plugin for IP Fabric" -category = "main" optional = true python-versions = ">=3.7.1,<4.0.0" files = [ @@ -2043,7 +1866,6 @@ PyJWT = ">=2.4.0,<3.0.0" name = "isodate" version = "0.6.1" description = "An ISO 8601 date/time/duration parser and formatter" -category = "main" optional = true python-versions = "*" files = [ @@ -2058,7 +1880,6 @@ six = "*" name = "isoduration" version = "20.11.0" description = "Operations with ISO 8601 durations" -category = "main" optional = true python-versions = ">=3.7" files = [ @@ -2073,7 +1894,6 @@ arrow = ">=0.15.0" name = "isort" version = "5.12.0" description = "A Python utility / library to sort Python imports." -category = "dev" optional = false python-versions = ">=3.8.0" files = [ @@ -2091,7 +1911,6 @@ requirements-deprecated-finder = ["pip-api", "pipreqs"] name = "jinja2" version = "3.1.2" description = "A very fast and expressive template engine." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2109,7 +1928,6 @@ i18n = ["Babel (>=2.7)"] name = "jmespath" version = "0.10.0" description = "JSON Matching Expressions" -category = "main" optional = true python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -2121,7 +1939,6 @@ files = [ name = "jsonpointer" version = "2.4" description = "Identify specific nodes in a JSON document (RFC 6901)" -category = "main" optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*" files = [ @@ -2133,7 +1950,6 @@ files = [ name = "jsonref" version = "0.2" description = "An implementation of JSON Reference for Python" -category = "main" optional = true python-versions = "*" files = [ @@ -2145,7 +1961,6 @@ files = [ name = "jsonschema" version = "4.17.3" description = "An implementation of JSON Schema validation for Python" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2175,7 +1990,6 @@ format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339- name = "kombu" version = "5.3.2" description = "Messaging library for Python." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -2210,7 +2024,6 @@ zookeeper = ["kazoo (>=2.8.0)"] name = "lazy-object-proxy" version = "1.9.0" description = "A fast and thorough lazy object proxy." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2256,7 +2069,6 @@ files = [ name = "macaddress" version = "2.0.2" description = "Like ``ipaddress``, but for hardware identifiers such as MAC addresses." -category = "main" optional = true python-versions = "*" files = [ @@ -2268,7 +2080,6 @@ files = [ name = "markdown" version = "3.3.7" description = "Python implementation of Markdown." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -2286,8 +2097,7 @@ testing = ["coverage", "pyyaml"] name = "markdown-it-py" version = "3.0.0" description = "Python port of markdown-it. Markdown parsing, done right!" -category = "main" -optional = false +optional = true python-versions = ">=3.8" files = [ {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, @@ -2311,7 +2121,6 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] name = "markupsafe" version = "2.1.3" description = "Safely add untrusted strings to HTML/XML markup." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2335,6 +2144,16 @@ files = [ {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, @@ -2371,7 +2190,6 @@ files = [ name = "mccabe" version = "0.6.1" description = "McCabe checker, plugin for flake8" -category = "dev" optional = false python-versions = "*" files = [ @@ -2383,8 +2201,7 @@ files = [ name = "mdurl" version = "0.1.2" description = "Markdown URL utilities" -category = "main" -optional = false +optional = true python-versions = ">=3.7" files = [ {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, @@ -2395,7 +2212,6 @@ files = [ name = "meraki" version = "1.37.3" description = "Cisco Meraki Dashboard API library" -category = "main" optional = true python-versions = ">=3.7" files = [ @@ -2411,7 +2227,6 @@ requests = "*" name = "mergedeep" version = "1.3.4" description = "A deep merge function for 🐍." -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -2423,7 +2238,6 @@ files = [ name = "mkdocs" version = "1.3.1" description = "Project documentation with Markdown." -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -2450,7 +2264,6 @@ i18n = ["babel (>=2.9.0)"] name = "mkdocs-autorefs" version = "0.5.0" description = "Automatically link across pages in MkDocs." -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -2466,7 +2279,6 @@ mkdocs = ">=1.1" name = "mkdocs-include-markdown-plugin" version = "3.6.1" description = "Mkdocs Markdown includer plugin." -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -2482,7 +2294,6 @@ test = ["pytest (==6.2.5)", "pytest-cov (==3.0.0)"] name = "mkdocs-material" version = "8.4.2" description = "Documentation that simply works" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2502,7 +2313,6 @@ pymdown-extensions = ">=9.4" name = "mkdocs-material-extensions" version = "1.2" description = "Extension pack for Python Markdown and MkDocs Material." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2514,7 +2324,6 @@ files = [ name = "mkdocs-version-annotations" version = "1.0.0" description = "MkDocs plugin to add custom admonitions for documenting version differences" -category = "dev" optional = false python-versions = ">=3.7,<4.0" files = [ @@ -2526,7 +2335,6 @@ files = [ name = "mkdocstrings" version = "0.22.0" description = "Automatic documentation from sources, for MkDocs." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2553,7 +2361,6 @@ python-legacy = ["mkdocstrings-python-legacy (>=0.2.1)"] name = "mkdocstrings-python" version = "1.1.2" description = "A Python handler for mkdocstrings." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2569,7 +2376,6 @@ mkdocstrings = ">=0.20" name = "msgpack" version = "1.0.7" description = "MessagePack serializer" -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -2635,7 +2441,6 @@ files = [ name = "multidict" version = "6.0.4" description = "multidict implementation" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2715,23 +2520,10 @@ files = [ {file = "multidict-6.0.4.tar.gz", hash = "sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49"}, ] -[[package]] -name = "mypy-extensions" -version = "1.0.0" -description = "Type system extensions for programs checked with the mypy type checker." -category = "dev" -optional = false -python-versions = ">=3.5" -files = [ - {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, - {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, -] - [[package]] name = "nautobot" version = "1.6.2" description = "Source of truth and network automation platform." -category = "main" optional = false python-versions = ">=3.8,<3.12" files = [ @@ -2796,7 +2588,6 @@ sso = ["social-auth-core[openidconnect,saml] (>=4.4.2,<4.5.0)"] name = "nautobot-capacity-metrics" version = "2.0.0" description = "Plugin to improve the instrumentation of Nautobot and expose additional metrics (Application Metrics, RQ Worker)." -category = "main" optional = false python-versions = ">=3.7,<4.0" files = [ @@ -2811,7 +2602,6 @@ nautobot = ">=1.2.0,<2.0.0" name = "netaddr" version = "0.8.0" description = "A network address manipulation library for Python" -category = "main" optional = false python-versions = "*" files = [ @@ -2823,7 +2613,6 @@ files = [ name = "netmiko" version = "4.2.0" description = "Multi-vendor library to simplify legacy CLI connections to network devices" -category = "main" optional = true python-versions = ">=3.7,<4.0" files = [ @@ -2843,7 +2632,6 @@ textfsm = ">=1.1.3" name = "netutils" version = "1.6.0" description = "Common helper functions useful in network automation." -category = "main" optional = false python-versions = ">=3.8,<4.0" files = [ @@ -2858,7 +2646,6 @@ optionals = ["jsonschema (>=4.17.3,<5.0.0)", "napalm (>=4.0.0,<5.0.0)"] name = "ntc-templates" version = "3.5.0" description = "TextFSM Templates for Network Devices, and Python wrapper for TextFSM's CliTable." -category = "main" optional = true python-versions = ">=3.7,<4.0" files = [ @@ -2873,7 +2660,6 @@ textfsm = ">=1.1.0,<2.0.0" name = "oauthlib" version = "3.2.2" description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -2890,7 +2676,6 @@ signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] name = "ordered-set" version = "4.1.0" description = "An OrderedSet is a custom MutableSet that remembers its order, so that every" -category = "main" optional = true python-versions = ">=3.7" files = [ @@ -2905,7 +2690,6 @@ dev = ["black", "mypy", "pytest"] name = "packaging" version = "23.1" description = "Core utilities for Python packages" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2917,7 +2701,6 @@ files = [ name = "pan-os-python" version = "1.11.0" description = "Framework for interacting with Palo Alto Networks devices via API" -category = "main" optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -2932,7 +2715,6 @@ pan-python = ">=0.17.0,<0.18.0" name = "pan-python" version = "0.17.0" description = "Multi-tool set for Palo Alto Networks PAN-OS, Panorama, WildFire and AutoFocus" -category = "main" optional = true python-versions = "*" files = [ @@ -2944,7 +2726,6 @@ files = [ name = "paramiko" version = "3.3.1" description = "SSH2 protocol library" -category = "main" optional = true python-versions = ">=3.6" files = [ @@ -2966,7 +2747,6 @@ invoke = ["invoke (>=2.0)"] name = "pathspec" version = "0.11.2" description = "Utility library for gitignore style pattern matching of file paths." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2974,23 +2754,10 @@ files = [ {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, ] -[[package]] -name = "pbr" -version = "5.11.1" -description = "Python Build Reasonableness" -category = "dev" -optional = false -python-versions = ">=2.6" -files = [ - {file = "pbr-5.11.1-py2.py3-none-any.whl", hash = "sha256:567f09558bae2b3ab53cb3c1e2e33e726ff3338e7bae3db5dc954b3a44eef12b"}, - {file = "pbr-5.11.1.tar.gz", hash = "sha256:aefc51675b0b533d56bb5fd1c8c6c0522fe31896679882e1c4c63d5e4a0fccb3"}, -] - [[package]] name = "pillow" version = "10.0.1" description = "Python Imaging Library (Fork)" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -3058,7 +2825,6 @@ tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "pa name = "pkgutil-resolve-name" version = "1.3.10" description = "Resolve a name to an object." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -3070,7 +2836,6 @@ files = [ name = "platformdirs" version = "3.10.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -3086,7 +2851,6 @@ test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-co name = "prettytable" version = "2.5.0" description = "A simple Python library for easily displaying tabular data in a visually appealing ASCII table format" -category = "main" optional = true python-versions = ">=3.6" files = [ @@ -3104,7 +2868,6 @@ tests = ["pytest", "pytest-cov", "pytest-lazy-fixture"] name = "prometheus-client" version = "0.17.1" description = "Python client for the Prometheus monitoring system." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -3119,7 +2882,6 @@ twisted = ["twisted"] name = "promise" version = "2.3" description = "Promises/A+ implementation for Python" -category = "main" optional = false python-versions = "*" files = [ @@ -3136,7 +2898,6 @@ test = ["coveralls", "futures", "mock", "pytest (>=2.7.3)", "pytest-benchmark", name = "prompt-toolkit" version = "3.0.39" description = "Library for building powerful interactive command lines in Python" -category = "main" optional = false python-versions = ">=3.7.0" files = [ @@ -3151,7 +2912,6 @@ wcwidth = "*" name = "protobuf" version = "3.20.3" description = "Protocol Buffers" -category = "main" optional = true python-versions = ">=3.7" files = [ @@ -3183,7 +2943,6 @@ files = [ name = "prybar" version = "1.0.0" description = "Create temporary pkg_resources entry points at runtime." -category = "dev" optional = false python-versions = "~=3.6" files = [ @@ -3200,7 +2959,6 @@ test = ["pytest", "pytest-cov"] name = "psycopg2-binary" version = "2.9.8" description = "psycopg2 - Python-PostgreSQL Database Adapter" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -3270,7 +3028,6 @@ files = [ name = "pycares" version = "4.3.0" description = "Python interface for c-ares" -category = "main" optional = false python-versions = "*" files = [ @@ -3334,23 +3091,10 @@ cffi = ">=1.5.0" [package.extras] idna = ["idna (>=2.1)"] -[[package]] -name = "pycodestyle" -version = "2.7.0" -description = "Python style guide checker" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, - {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, -] - [[package]] name = "pycparser" version = "2.21" description = "C parser in Python" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -3362,7 +3106,6 @@ files = [ name = "pydantic" version = "1.10.13" description = "Data validation and settings management using python type hints" -category = "main" optional = true python-versions = ">=3.7" files = [ @@ -3411,41 +3154,10 @@ typing-extensions = ">=4.2.0" dotenv = ["python-dotenv (>=0.10.4)"] email = ["email-validator (>=1.0.3)"] -[[package]] -name = "pydocstyle" -version = "6.3.0" -description = "Python docstring style checker" -category = "dev" -optional = false -python-versions = ">=3.6" -files = [ - {file = "pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019"}, - {file = "pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1"}, -] - -[package.dependencies] -snowballstemmer = ">=2.2.0" - -[package.extras] -toml = ["tomli (>=1.2.3)"] - -[[package]] -name = "pyflakes" -version = "2.3.1" -description = "passive checker of Python programs" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, - {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, -] - [[package]] name = "pygments" version = "2.16.1" description = "Pygments is a syntax highlighting package written in Python." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -3460,7 +3172,6 @@ plugins = ["importlib-metadata"] name = "pyjwt" version = "2.8.0" description = "JSON Web Token implementation in Python" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -3478,7 +3189,6 @@ tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] name = "pylint" version = "2.13.9" description = "python code static checker" -category = "dev" optional = false python-versions = ">=3.6.2" files = [ @@ -3503,7 +3213,6 @@ testutil = ["gitpython (>3)"] name = "pylint-django" version = "2.5.3" description = "A Pylint plugin to help Pylint understand the Django web framework" -category = "dev" optional = false python-versions = "*" files = [ @@ -3523,7 +3232,6 @@ with-django = ["Django"] name = "pylint-plugin-utils" version = "0.8.2" description = "Utilities and helpers for writing Pylint plugins" -category = "dev" optional = false python-versions = ">=3.7,<4.0" files = [ @@ -3538,7 +3246,6 @@ pylint = ">=1.7" name = "pymdown-extensions" version = "10.3" description = "Extension pack for Python Markdown." -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -3557,7 +3264,6 @@ extra = ["pygments (>=2.12)"] name = "pynacl" version = "1.5.0" description = "Python binding to the Networking and Cryptography (NaCl) library" -category = "main" optional = true python-versions = ">=3.6" files = [ @@ -3584,7 +3290,6 @@ tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"] name = "pyrsistent" version = "0.19.3" description = "Persistent/Functional/Immutable data structures" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -3621,7 +3326,6 @@ files = [ name = "pyserial" version = "3.5" description = "Python Serial Port Extension" -category = "main" optional = true python-versions = "*" files = [ @@ -3636,7 +3340,6 @@ cp2110 = ["hidapi"] name = "pysocks" version = "1.7.1" description = "A Python SOCKS client module. See https://github.com/Anorov/PySocks for more information." -category = "main" optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -3649,7 +3352,6 @@ files = [ name = "python-crontab" version = "3.0.0" description = "Python Crontab API" -category = "main" optional = false python-versions = "*" files = [ @@ -3668,7 +3370,6 @@ cron-schedule = ["croniter"] name = "python-dateutil" version = "2.8.2" description = "Extensions to the standard Python datetime module" -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ @@ -3683,7 +3384,6 @@ six = ">=1.5" name = "python-dotenv" version = "0.21.1" description = "Read key-value pairs from a .env file and set them as environment variables" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -3698,7 +3398,6 @@ cli = ["click (>=5.0)"] name = "python3-openid" version = "3.2.0" description = "OpenID support for modern servers and consumers." -category = "main" optional = false python-versions = "*" files = [ @@ -3717,7 +3416,6 @@ postgresql = ["psycopg2"] name = "pytz" version = "2022.7.1" description = "World timezone definitions, modern and historical" -category = "main" optional = false python-versions = "*" files = [ @@ -3729,7 +3427,6 @@ files = [ name = "pyuwsgi" version = "2.0.22" description = "The uWSGI server" -category = "main" optional = false python-versions = "*" files = [ @@ -3782,7 +3479,6 @@ files = [ name = "pyyaml" version = "6.0.1" description = "YAML parser and emitter for Python" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -3791,6 +3487,7 @@ files = [ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, @@ -3798,8 +3495,16 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, @@ -3816,6 +3521,7 @@ files = [ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, @@ -3823,6 +3529,7 @@ files = [ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, @@ -3832,7 +3539,6 @@ files = [ name = "pyyaml-env-tag" version = "0.1" description = "A custom YAML tag for referencing environment variables in YAML files. " -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -3847,7 +3553,6 @@ pyyaml = "*" name = "redis" version = "5.0.1" description = "Python client for Redis database and key-value store" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -3866,7 +3571,6 @@ ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)" name = "requests" version = "2.31.0" description = "Python HTTP for Humans." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -3889,7 +3593,6 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] name = "requests-mock" version = "1.11.0" description = "Mock out responses from the requests package" -category = "dev" optional = false python-versions = "*" files = [ @@ -3909,7 +3612,6 @@ test = ["fixtures", "mock", "purl", "pytest", "requests-futures", "sphinx", "tes name = "requests-oauthlib" version = "1.3.1" description = "OAuthlib authentication support for Requests." -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -3928,7 +3630,6 @@ rsa = ["oauthlib[signedtoken] (>=3.0.0)"] name = "requests-toolbelt" version = "1.0.0" description = "A utility belt for advanced users of python-requests" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -3943,7 +3644,6 @@ requests = ">=2.0.1,<3.0.0" name = "rfc3339-validator" version = "0.1.4" description = "A pure python RFC3339 validator" -category = "main" optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -3958,7 +3658,6 @@ six = "*" name = "rfc3986" version = "1.5.0" description = "Validating URI References per RFC 3986" -category = "main" optional = true python-versions = "*" files = [ @@ -3976,7 +3675,6 @@ idna2008 = ["idna"] name = "rfc3986-validator" version = "0.1.1" description = "Pure python rfc3986 validator" -category = "main" optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -3988,8 +3686,7 @@ files = [ name = "rich" version = "13.5.3" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" -category = "main" -optional = false +optional = true python-versions = ">=3.7.0" files = [ {file = "rich-13.5.3-py3-none-any.whl", hash = "sha256:9257b468badc3d347e146a4faa268ff229039d4c2d176ab0cffb4c4fbc73d5d9"}, @@ -4008,7 +3705,6 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] name = "rq" version = "1.15.1" description = "RQ is a simple, lightweight, library for creating background jobs, and processing them." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -4024,7 +3720,6 @@ redis = ">=4.0.0" name = "ruamel-yaml" version = "0.17.33" description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" -category = "main" optional = true python-versions = ">=3" files = [ @@ -4043,7 +3738,6 @@ jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] name = "ruamel-yaml-clib" version = "0.2.7" description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" -category = "main" optional = true python-versions = ">=3.5" files = [ @@ -4086,11 +3780,37 @@ files = [ {file = "ruamel.yaml.clib-0.2.7.tar.gz", hash = "sha256:1f08fd5a2bea9c4180db71678e850b995d2a5f4537be0e94557668cf0f5f9497"}, ] +[[package]] +name = "ruff" +version = "0.5.5" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.5.5-py3-none-linux_armv6l.whl", hash = "sha256:605d589ec35d1da9213a9d4d7e7a9c761d90bba78fc8790d1c5e65026c1b9eaf"}, + {file = "ruff-0.5.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:00817603822a3e42b80f7c3298c8269e09f889ee94640cd1fc7f9329788d7bf8"}, + {file = "ruff-0.5.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:187a60f555e9f865a2ff2c6984b9afeffa7158ba6e1eab56cb830404c942b0f3"}, + {file = "ruff-0.5.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe26fc46fa8c6e0ae3f47ddccfbb136253c831c3289bba044befe68f467bfb16"}, + {file = "ruff-0.5.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4ad25dd9c5faac95c8e9efb13e15803cd8bbf7f4600645a60ffe17c73f60779b"}, + {file = "ruff-0.5.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f70737c157d7edf749bcb952d13854e8f745cec695a01bdc6e29c29c288fc36e"}, + {file = "ruff-0.5.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:cfd7de17cef6ab559e9f5ab859f0d3296393bc78f69030967ca4d87a541b97a0"}, + {file = "ruff-0.5.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a09b43e02f76ac0145f86a08e045e2ea452066f7ba064fd6b0cdccb486f7c3e7"}, + {file = "ruff-0.5.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d0b856cb19c60cd40198be5d8d4b556228e3dcd545b4f423d1ad812bfdca5884"}, + {file = "ruff-0.5.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3687d002f911e8a5faf977e619a034d159a8373514a587249cc00f211c67a091"}, + {file = "ruff-0.5.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ac9dc814e510436e30d0ba535f435a7f3dc97f895f844f5b3f347ec8c228a523"}, + {file = "ruff-0.5.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:af9bdf6c389b5add40d89b201425b531e0a5cceb3cfdcc69f04d3d531c6be74f"}, + {file = "ruff-0.5.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d40a8533ed545390ef8315b8e25c4bb85739b90bd0f3fe1280a29ae364cc55d8"}, + {file = "ruff-0.5.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:cab904683bf9e2ecbbe9ff235bfe056f0eba754d0168ad5407832928d579e7ab"}, + {file = "ruff-0.5.5-py3-none-win32.whl", hash = "sha256:696f18463b47a94575db635ebb4c178188645636f05e934fdf361b74edf1bb2d"}, + {file = "ruff-0.5.5-py3-none-win_amd64.whl", hash = "sha256:50f36d77f52d4c9c2f1361ccbfbd09099a1b2ea5d2b2222c586ab08885cf3445"}, + {file = "ruff-0.5.5-py3-none-win_arm64.whl", hash = "sha256:3191317d967af701f1b73a31ed5788795936e423b7acce82a2b63e26eb3e89d6"}, + {file = "ruff-0.5.5.tar.gz", hash = "sha256:cc5516bdb4858d972fbc31d246bdb390eab8df1a26e2353be2dbc0c2d7f5421a"}, +] + [[package]] name = "rx" version = "1.6.3" description = "Reactive Extensions (Rx) for Python" -category = "main" optional = false python-versions = "*" files = [ @@ -4101,7 +3821,6 @@ files = [ name = "schema-enforcer" version = "1.2.2" description = "Tool/Framework for testing structured data against schema definitions" -category = "main" optional = true python-versions = ">=3.8,<4.0" files = [ @@ -4130,7 +3849,6 @@ ansible-base = ["ansible-base (>=2.10.0,<3.0.0)"] name = "scp" version = "0.14.5" description = "scp module for paramiko" -category = "main" optional = true python-versions = "*" files = [ @@ -4145,7 +3863,6 @@ paramiko = "*" name = "setuptools" version = "68.2.2" description = "Easily download, build, install, upgrade, and uninstall Python packages" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -4162,7 +3879,6 @@ testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jar name = "singledispatch" version = "4.1.0" description = "Backport functools.singledispatch to older Pythons." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -4178,7 +3894,6 @@ testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", name = "six" version = "1.16.0" description = "Python 2 and 3 compatibility utilities" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -4190,7 +3905,6 @@ files = [ name = "slack-sdk" version = "3.22.0" description = "The Slack API Platform SDK for Python" -category = "main" optional = false python-versions = ">=3.6.0" files = [ @@ -4206,7 +3920,6 @@ testing = ["Flask (>=1,<2)", "Flask-Sockets (>=0.2,<1)", "Jinja2 (==3.0.3)", "We name = "smmap" version = "5.0.1" description = "A pure Python implementation of a sliding window memory map manager" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4218,7 +3931,6 @@ files = [ name = "sniffio" version = "1.3.0" description = "Sniff out which async library your code is running under" -category = "main" optional = true python-versions = ">=3.7" files = [ @@ -4226,23 +3938,10 @@ files = [ {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, ] -[[package]] -name = "snowballstemmer" -version = "2.2.0" -description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." -category = "dev" -optional = false -python-versions = "*" -files = [ - {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, - {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, -] - [[package]] name = "social-auth-app-django" version = "5.2.0" description = "Python Social Authentication, Django integration." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4258,7 +3957,6 @@ social-auth-core = ">=4.4.1" name = "social-auth-core" version = "4.4.2" description = "Python social authentication made simple." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -4286,7 +3984,6 @@ saml = ["python3-saml (>=1.5.0)"] name = "sqlparse" version = "0.4.4" description = "A non-validating SQL parser." -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -4299,26 +3996,10 @@ dev = ["build", "flake8"] doc = ["sphinx"] test = ["pytest", "pytest-cov"] -[[package]] -name = "stevedore" -version = "5.1.0" -description = "Manage dynamic plugins for Python applications" -category = "dev" -optional = false -python-versions = ">=3.8" -files = [ - {file = "stevedore-5.1.0-py3-none-any.whl", hash = "sha256:8cc040628f3cea5d7128f2e76cf486b2251a4e543c7b938f58d9a377f6694a2d"}, - {file = "stevedore-5.1.0.tar.gz", hash = "sha256:a54534acf9b89bc7ed264807013b505bf07f74dbe4bcfa37d32bd063870b087c"}, -] - -[package.dependencies] -pbr = ">=2.0.0,<2.1.0 || >2.1.0" - [[package]] name = "structlog" version = "22.3.0" description = "Structured Logging for Python" -category = "main" optional = true python-versions = ">=3.7" files = [ @@ -4336,7 +4017,6 @@ typing = ["mypy", "rich", "twisted"] name = "svgwrite" version = "1.4.3" description = "A Python library to create SVG drawings." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -4348,7 +4028,6 @@ files = [ name = "swagger-spec-validator" version = "3.0.3" description = "Validation of Swagger specifications" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4365,7 +4044,6 @@ typing-extensions = "*" name = "termcolor" version = "1.1.0" description = "ANSII Color formatting for output in terminal." -category = "main" optional = true python-versions = "*" files = [ @@ -4376,7 +4054,6 @@ files = [ name = "text-unidecode" version = "1.3" description = "The most basic Text::Unidecode port" -category = "main" optional = false python-versions = "*" files = [ @@ -4388,7 +4065,6 @@ files = [ name = "textfsm" version = "1.1.3" description = "Python module for parsing semi-structured text into python tables." -category = "main" optional = true python-versions = "*" files = [ @@ -4404,7 +4080,6 @@ six = "*" name = "texttable" version = "1.6.7" description = "module to create simple ASCII tables" -category = "main" optional = false python-versions = "*" files = [ @@ -4416,7 +4091,6 @@ files = [ name = "toml" version = "0.10.2" description = "Python Library for Tom's Obvious, Minimal Language" -category = "main" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -4428,7 +4102,6 @@ files = [ name = "tomli" version = "2.0.1" description = "A lil' TOML parser" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -4440,7 +4113,6 @@ files = [ name = "towncrier" version = "22.8.0" description = "Building newsfiles for your project." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -4463,7 +4135,6 @@ dev = ["packaging"] name = "types-protobuf" version = "3.20.4.6" description = "Typing stubs for protobuf" -category = "main" optional = true python-versions = "*" files = [ @@ -4475,7 +4146,6 @@ files = [ name = "types-pyyaml" version = "6.0.12.12" description = "Typing stubs for PyYAML" -category = "main" optional = true python-versions = "*" files = [ @@ -4487,7 +4157,6 @@ files = [ name = "types-requests" version = "2.31.0.6" description = "Typing stubs for requests" -category = "main" optional = true python-versions = ">=3.7" files = [ @@ -4502,7 +4171,6 @@ types-urllib3 = "*" name = "types-urllib3" version = "1.26.25.14" description = "Typing stubs for urllib3" -category = "main" optional = true python-versions = "*" files = [ @@ -4514,7 +4182,6 @@ files = [ name = "typing" version = "3.7.4.3" description = "Type Hints for Python" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -4526,7 +4193,6 @@ files = [ name = "typing-extensions" version = "4.8.0" description = "Backported and Experimental Type Hints for Python 3.8+" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -4538,7 +4204,6 @@ files = [ name = "tzdata" version = "2023.3" description = "Provider of IANA time zone data" -category = "main" optional = false python-versions = ">=2" files = [ @@ -4550,7 +4215,6 @@ files = [ name = "uri-template" version = "1.3.0" description = "RFC 6570 URI Template Processor" -category = "main" optional = true python-versions = ">=3.7" files = [ @@ -4565,7 +4229,6 @@ dev = ["flake8", "flake8-annotations", "flake8-bandit", "flake8-bugbear", "flake name = "uritemplate" version = "4.1.1" description = "Implementation of RFC 6570 URI Templates" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -4577,7 +4240,6 @@ files = [ name = "urllib3" version = "2.0.5" description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4595,7 +4257,6 @@ zstd = ["zstandard (>=0.18.0)"] name = "vine" version = "5.0.0" description = "Promises, promises, promises." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -4607,7 +4268,6 @@ files = [ name = "watchdog" version = "3.0.0" description = "Filesystem events monitoring" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -4647,7 +4307,6 @@ watchmedo = ["PyYAML (>=3.10)"] name = "wcwidth" version = "0.2.6" description = "Measures the displayed width of unicode strings in a terminal" -category = "main" optional = false python-versions = "*" files = [ @@ -4659,7 +4318,6 @@ files = [ name = "webcolors" version = "1.13" description = "A library for working with the color formats defined by HTML and CSS." -category = "main" optional = true python-versions = ">=3.7" files = [ @@ -4675,7 +4333,6 @@ tests = ["pytest", "pytest-cov"] name = "webexteamssdk" version = "1.6.1" description = "Community-developed Python SDK for the Webex Teams APIs" -category = "main" optional = false python-versions = "*" files = [ @@ -4693,7 +4350,6 @@ requests-toolbelt = "*" name = "wrapt" version = "1.15.0" description = "Module for decorators, wrappers and monkey patching." -category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" files = [ @@ -4778,7 +4434,6 @@ files = [ name = "yamllint" version = "1.32.0" description = "A linter for YAML files." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -4797,7 +4452,6 @@ dev = ["doc8", "flake8", "flake8-import-order", "rstcheck[sphinx]", "sphinx"] name = "yarl" version = "1.9.2" description = "Yet another URL library" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4885,7 +4539,6 @@ multidict = ">=4.0" name = "zipp" version = "3.17.0" description = "Backport of pathlib-compatible object wrapper for zip files" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -4911,4 +4564,4 @@ panorama = ["defusedxml", "ipaddr", "netmiko", "netutils", "pan-os-python"] [metadata] lock-version = "2.0" python-versions = ">=3.8,<3.12" -content-hash = "e1063f1c21d1ea6234601899a7d2058eb4a7bc28eff06af433be54af376bd963" +content-hash = "d535898f519b0fb45843c5a48ebe94311c09e01bb1a9caff347d930c4a2820a3" diff --git a/pyproject.toml b/pyproject.toml index ad64a549..b5ed4c44 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,7 +42,7 @@ ipaddr = { version = "^2.2.0", optional = true } ipfabric = { version = "~6.0.9", optional = true } ipfabric-diagrams = { version = "~6.0.2", optional = true } isodate = { version = "^0.6.1", optional = true } -meraki = { version = "^1.7.2", optional = true } +meraki = { version = ">=1.7.2,<=1.45.0", optional = true } nautobot = "^1.6.2" nautobot-capacity-metrics = "^2.0.0" netmiko = { version = "^4.0.0", optional = true } @@ -60,16 +60,12 @@ texttable = "^1.6.2" webexteamssdk = "^1.3" [tool.poetry.dev-dependencies] -black = "*" yamllint = "*" -bandit = "*" # Pinning older pylint due to https://github.com/pylint-dev/pylint/issues/7381 pylint = "2.13.9" pylint-django = "*" -pydocstyle = "*" prybar = "*" invoke = "*" -flake8 = "^3.9.2" griffe = "0.30.1" # Rendering docs to HTML mkdocs = "1.3.1" @@ -83,6 +79,7 @@ mkdocs-version-annotations = "~1.0.0" # Allow Markdown files to include other files mkdocs-include-markdown-plugin = "~3.6.1" python-dotenv = "^0.21.1" +ruff = "0.5.5" # Change log management and generation towncrier = "~22.8.0" coverage = "~5.4" @@ -175,7 +172,7 @@ load-plugins="pylint_django" [tool.pylint.message_control] disable=""", - django-not-configured, + line-too-long, too-few-public-methods, too-many-lines, """ @@ -197,6 +194,48 @@ min-similarity-lines=0 [tool.pylint.format] max-line-length=120 +[tool.ruff] +line-length = 120 +target-version = "py38" + +[tool.ruff.lint] +select = [ + "D", # pydocstyle + "F", "E", "W", # flake8 + "S", # bandit + "I", # isort +] +ignore = [ + # warning: `one-blank-line-before-class` (D203) and `no-blank-line-before-class` (D211) are incompatible. + "D203", # 1 blank line required before class docstring + + # D212 is enabled by default in google convention, and complains if we have a docstring like: + # """ + # My docstring is on the line after the opening quotes instead of on the same line as them. + # """ + # We've discussed and concluded that we consider this to be a valid style choice. + "D212", # Multi-line docstring summary should start at the first line + "D213", # Multi-line docstring summary should start at the second line + + # Produces a lot of issues in the current codebase. + "D401", # First line of docstring should be in imperative mood + "D407", # Missing dashed underline after section + "D416", # Section name ends in colon + "E501", # Line too long +] + +[tool.ruff.lint.pydocstyle] +convention = "google" + +[tool.ruff.lint.per-file-ignores] +"nautobot_chatops/migrations/*" = [ + "D", +] +"nautobot_chatops/tests/*" = [ + "D", + "S" +] + [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" diff --git a/tasks.py b/tasks.py index 87eb91a0..b964093b 100644 --- a/tasks.py +++ b/tasks.py @@ -12,16 +12,10 @@ limitations under the License. """ -from distutils.util import strtobool -from invoke import Collection, task as invoke_task import os -from dotenv import load_dotenv - - -def _load_dotenv(): - load_dotenv("./development/development.env") - load_dotenv("./development/creds.env") +from invoke import Collection +from invoke import task as invoke_task def is_truthy(arg): @@ -36,7 +30,14 @@ def is_truthy(arg): """ if isinstance(arg, bool): return arg - return bool(strtobool(arg)) + + val = str(arg).lower() + if val in ("y", "yes", "t", "true", "on", "1"): + return True + elif val in ("n", "no", "f", "false", "off", "0"): + return False + else: + raise ValueError(f"Invalid truthy value: `{arg}`") # Use pyinvoke configuration for default values, see http://docs.pyinvoke.org/en/stable/concepts/configuration.html @@ -375,8 +376,6 @@ def dbshell(context, query="", input="", output=""): if output and not (input or query): raise ValueError("`output` argument requires `input` or `query` argument") - _load_dotenv() - service = "db" env_vars = {} command = ["exec"] @@ -422,9 +421,6 @@ def dbshell(context, query="", input="", output=""): def import_db(context, input="dump.sql"): """Stop Nautobot containers and replace the current database with the dump into the running `db` container.""" docker_compose(context, "stop -- nautobot worker") - - _load_dotenv() - service = "db" env_vars = {} command = ["exec"] @@ -465,8 +461,6 @@ def import_db(context, input="dump.sql"): ) def backup_db(context, output="dump.sql", readable=True): """Dump database into `output` file from running `db` container.""" - _load_dotenv() - service = "db" env_vars = {} command = ["exec"] @@ -529,30 +523,6 @@ def help_task(context): # ------------------------------------------------------------------------------ # TESTS # ------------------------------------------------------------------------------ -@task( - help={ - "autoformat": "Apply formatting recommendations automatically, rather than failing if formatting is incorrect.", - } -) -def black(context, autoformat=False): - """Check Python code style with Black.""" - if autoformat: - black_command = "black" - else: - black_command = "black --check --diff" - - command = f"{black_command} ." - - run_command(context, command) - - -@task -def flake8(context): - """Check for PEP8 compliance and other style issues.""" - command = "flake8 . --config .flake8" - run_command(context, command) - - @task def hadolint(context): """Check Dockerfile for hadolint compliance and other style issues.""" @@ -567,19 +537,44 @@ def pylint(context): run_command(context, command) -@task -def pydocstyle(context): - """Run pydocstyle to validate docstring formatting adheres to NTC defined standards.""" - # We exclude the /migrations/ directory since it is autogenerated code - command = 'pydocstyle --config=.pydocstyle.ini --match-dir="^(?!migrations).*"' - run_command(context, command) +task(aliases=("a",)) -@task -def bandit(context): - """Run bandit to validate basic static code security analysis.""" - command = "bandit --recursive . --configfile .bandit.yml" - run_command(context, command) +def autoformat(context): + """Run code autoformatting.""" + ruff(context, action=["format"], fix=True) + + +@task( + help={ + "action": "Available values are `['lint', 'format']`. Can be used multiple times. (default: `['lint', 'format']`)", + "target": "File or directory to inspect, repeatable (default: all files in the project will be inspected)", + "fix": "Automatically fix selected actions. May not be able to fix all issues found. (default: False)", + "output_format": "See https://docs.astral.sh/ruff/settings/#output-format for details. (default: `concise`)", + }, + iterable=["action", "target"], +) +def ruff(context, action=None, target=None, fix=False, output_format="concise"): + """Run ruff to perform code formatting and/or linting.""" + if not action: + action = ["lint", "format"] + if not target: + target = ["."] + + if "format" in action: + command = "ruff format " + if not fix: + command += "--check " + command += " ".join(target) + run_command(context, command, warn=True) + + if "lint" in action: + command = "ruff check " + if fix: + command += "--fix " + command += f"--output-format {output_format} " + command += " ".join(target) + run_command(context, command, warn=True) @task @@ -652,14 +647,8 @@ def tests(context, failfast=False): print("Starting Docker Containers...") start(context) # Sorted loosely from fastest to slowest - print("Running black...") - black(context) - print("Running flake8...") - flake8(context) - print("Running bandit...") - bandit(context) - print("Running pydocstyle...") - pydocstyle(context) + print("Running ruff...") + ruff(context) print("Running yamllint...") yamllint(context) print("Running pylint...")