From 8ad2c594ecb680847436e2f7ad0fb6ea260f8295 Mon Sep 17 00:00:00 2001 From: Carlos Huaccho Date: Tue, 1 Apr 2025 23:42:13 -0400 Subject: [PATCH 1/7] Updating portion of the docs. Need to resume --- hier_config/platforms/__init__.py | 1 + hier_config/platforms/driver_base.py | 90 ++++++++++++++++++++++++++-- hier_config/platforms/functions.py | 41 +++++++++---- hier_config/platforms/view_base.py | 12 +++- 4 files changed, 126 insertions(+), 18 deletions(-) diff --git a/hier_config/platforms/__init__.py b/hier_config/platforms/__init__.py index e69de29b..11605a4f 100644 --- a/hier_config/platforms/__init__.py +++ b/hier_config/platforms/__init__.py @@ -0,0 +1 @@ +"""Initialize all platforms.""" diff --git a/hier_config/platforms/driver_base.py b/hier_config/platforms/driver_base.py index 12ba5a9e..160a0669 100644 --- a/hier_config/platforms/driver_base.py +++ b/hier_config/platforms/driver_base.py @@ -24,6 +24,27 @@ class HConfigDriverRules(BaseModel): # pylint: disable=too-many-instance-attributes + """Configuration rules used by a driver to control parsing, matching, + ordering, rendering, and normalization of device configurations. + + Attributes: + full_text_sub: Substitutions applied across full config blocks. + idempotent_commands: Commands safe to appear multiple times. + idempotent_commands_avoid: Commands that should not be repeated. + indent_adjust: Manual indentation overrides for specific lines. + indentation: Number of spaces used for indentation. + negation_default_when: When to apply 'no' for default/removed lines. + negate_with: Custom negation rules for specific commands. + ordering: Defines sort order of config sections and lines. + parent_allows_duplicate_child: Allows child repetition under a parent. + per_line_sub: Line-based text substitutions. + post_load_callbacks: Functions run after config is loaded. + sectional_exiting: Rules for determining end of a config block. + sectional_overwrite: Replace full blocks instead of diffing lines. + sectional_overwrite_no_negate: Overwrite blocks without using 'no'. + + """ + full_text_sub: list[FullTextSubRule] = Field(default_factory=list) idempotent_commands: list[IdempotentCommandsRule] = Field(default_factory=list) idempotent_commands_avoid: list[IdempotentCommandsAvoidRule] = Field( @@ -52,28 +73,57 @@ class HConfigDriverBase(ABC): """ def __init__(self) -> None: - self.rules = self._instantiate_rules() + """Initialize the base driver.""" + self.rules: HConfigDriverRules = self._instantiate_rules() def idempotent_for( self, config: HConfigChild, other_children: Iterable[HConfigChild], ) -> Optional[HConfigChild]: + """Determine if the `config` is an idempotent change. + + Args: + config (HConfigChild): The child config object to check. + other_children (Iterable[HConfigChild]): Other children to check. + + Returns: + Optional[HConfigChild]: The other child that is an idempotent + change. None if not found. + + """ for rule in self.rules.idempotent_commands: - if config.is_lineage_match(rule.match_rules): + if config.is_lineage_match(rules=rule.match_rules): for other_child in other_children: - if other_child.is_lineage_match(rule.match_rules): + if other_child.is_lineage_match(rules=rule.match_rules): return other_child return None def negate_with(self, config: HConfigChild) -> Optional[str]: + """Determine if the `config` should be negated. + + Args: + config (HConfigChild): The child config object to check. + + Returns: + Optional[str]: _description_ + + """ for with_rule in self.rules.negate_with: - if config.is_lineage_match(with_rule.match_rules): + if config.is_lineage_match(rules=with_rule.match_rules): return with_rule.use return None def swap_negation(self, child: HConfigChild) -> HConfigChild: - """Swap negation of a `child.text`.""" + """Swap negation of a `child.text`. + + Args: + child (HConfigChild): The child config object to check. + + Returns: + HConfigChild: The child config object with negation swapped. + + """ if child.text.startswith(self.negation_prefix): child.text = child.text_without_negation else: @@ -83,17 +133,45 @@ def swap_negation(self, child: HConfigChild) -> HConfigChild: @property def declaration_prefix(self) -> str: + """Returns the prefix used for command declarations (e.g., 'no', 'delete'). + + Default is an empty string; meant to be overridden by platform-specific drivers. + """ return "" @property def negation_prefix(self) -> str: + """Returns the default prefix used to negate configuration commands. + + Default is 'no ', commonly used in IOS-style configurations. + """ return "no " @staticmethod def config_preprocessor(config_text: str) -> str: + """Preprocesses raw configuration text before parsing. + + By default, returns the input unchanged. Subclasses can override + this to normalize, clean, or transform config text as needed. + + Args: + config_text: The raw configuration string. + + Returns: + str: The (optionally modified) configuration string. + + """ return config_text @staticmethod @abstractmethod def _instantiate_rules() -> HConfigDriverRules: - pass + """Creates and returns a set of HConfigDriverRules for the driver. + + This must be implemented by each driver subclass to define the platform-specific + parsing, rendering, and normalization behavior. + + Returns: + HConfigDriverRules: A complete ruleset for the configuration driver. + + """ diff --git a/hier_config/platforms/functions.py b/hier_config/platforms/functions.py index 3c1c6206..f571b4e0 100644 --- a/hier_config/platforms/functions.py +++ b/hier_config/platforms/functions.py @@ -1,16 +1,33 @@ def expand_range(number_range_str: str) -> tuple[int, ...]: - """Expand ranges like 2-5,8,22-45.""" + """Expand ranges like 2-5,8,22-45. + + Args: + number_range_str (str): The range to expand. + + Raises: + ValueError: The range of integers are invalid. + Should be of the form 2-5,8,22-45. + ValueError: The length of unique integers is not the same as the + length of all integers. + + Returns: + tuple[int, ...]: Tuple of integers. + + """ numbers: list[int] = [] - for number_range in number_range_str.split(","): - start_stop = number_range.split("-") + for number_range in number_range_str.split(sep=","): + start_stop: list[str] = number_range.split(sep="-") if len(start_stop) == 2: start = int(start_stop[0]) stop = int(start_stop[1]) numbers.extend(n for n in range(start, stop + 1)) - else: + if len(start_stop) == 1: numbers.append(int(start_stop[0])) + else: + message: str = f"Invalid range: {number_range}" + raise ValueError(message) if len(set(numbers)) != len(numbers): - message = "len(set(numbers)) must be equal to len(numbers)." + message: str = "len(set(numbers)) must be equal to len(numbers)." raise ValueError(message) return tuple(numbers) @@ -24,12 +41,16 @@ def convert_to_set_commands(config_raw: str) -> str: config_raw (str): Configuration string """ - lines = config_raw.split("\n") + lines: list[str] = config_raw.split(sep="\n") + + # List of paths to the current command path: list[str] = [] + + # The list of actual configuration commands set_commands: list[str] = [] for line in lines: - stripped_line = line.strip() + stripped_line: str = line.strip() # Skip empty lines if not stripped_line: @@ -37,10 +58,10 @@ def convert_to_set_commands(config_raw: str) -> str: # Strip ; from the end of the line if stripped_line.endswith(";"): - stripped_line = stripped_line.replace(";", "") + stripped_line: str = stripped_line.replace(";", "") # Count the number of spaces at the beginning to determine the level - level = line.find(stripped_line) // 4 + level: int = line.find(stripped_line) // 4 # Adjust the current path based on the level path = path[:level] @@ -53,7 +74,7 @@ def convert_to_set_commands(config_raw: str) -> str: set_commands.append(stripped_line) else: # It's a command line, construct the full command - command = f"set {' '.join(path)} {stripped_line}" + command: str = f"set {' '.join(path)} {stripped_line}" set_commands.append(command) return "\n".join(set_commands) diff --git a/hier_config/platforms/view_base.py b/hier_config/platforms/view_base.py index be53b206..19f7d5e3 100644 --- a/hier_config/platforms/view_base.py +++ b/hier_config/platforms/view_base.py @@ -15,8 +15,16 @@ class ConfigViewInterfaceBase: # noqa: PLR0904 + """Abstract base class for extracting structured interface data from an HConfigChild node.""" + def __init__(self, config: HConfigChild) -> None: - self.config = config + """Initialize the interface view. + + Args: + config (HConfigChild): The child config object to view. + + """ + self.config: HConfigChild = config @property @abstractmethod @@ -187,7 +195,7 @@ def _bundle_prefix(self) -> str: class HConfigViewBase(ABC): def __init__(self, config: HConfig) -> None: - self.config = config + self.config: HConfig = config @property def bundle_interface_views(self) -> Iterable[ConfigViewInterfaceBase]: From 485debecdf54d35807022d930bc844bdef9db9ad Mon Sep 17 00:00:00 2001 From: Carlos Huaccho Date: Thu, 3 Apr 2025 01:02:01 -0400 Subject: [PATCH 2/7] NEED TO DO: platforms.view_base.ConfigViewInterfaceBase.ipv4_interfaces --- hier_config/.gitignore | 1 + hier_config/platforms/view_base.py | 321 +++++++++++++++++++++++++---- 2 files changed, 282 insertions(+), 40 deletions(-) create mode 100644 hier_config/.gitignore diff --git a/hier_config/.gitignore b/hier_config/.gitignore new file mode 100644 index 00000000..45784b06 --- /dev/null +++ b/hier_config/.gitignore @@ -0,0 +1 @@ +.qodo diff --git a/hier_config/platforms/view_base.py b/hier_config/platforms/view_base.py index 19f7d5e3..532b5ffc 100644 --- a/hier_config/platforms/view_base.py +++ b/hier_config/platforms/view_base.py @@ -29,26 +29,51 @@ def __init__(self, config: HConfigChild) -> None: @property @abstractmethod def bundle_id(self) -> Optional[str]: - """Determine the bundle ID.""" + """Determine the bundle (or LAG) ID. + + Returns: + Optional[str]: The bundle (or LAG) ID. + + """ @property @abstractmethod def bundle_member_interfaces(self) -> Iterable[str]: - """Determine the member interfaces of a bundle.""" + """Determine the member interfaces of a bundle. + + Returns: + Iterable[str]: The member interfaces of a bundle. + + """ @property @abstractmethod def bundle_name(self) -> Optional[str]: - """Determine the bundle name of a bundle member.""" + """Determine the bundle name of a bundle member. + + Returns: + Optional[str]: The bundle name of a bundle member or None. + + """ @property @abstractmethod def description(self) -> str: - """Determine the interface's description.""" + """Determine the interface's description. + + Returns: + str: The interface's description. + + """ @property def dot1q_mode(self) -> Optional[InterfaceDot1qMode]: - """Derive the configured 802.1Q mode.""" + """Derive the configured 802.1Q mode. + + Returns: + Optional[InterfaceDot1qMode]: The configured 802.1Q mode. + + """ if self.tagged_all: return InterfaceDot1qMode.TAGGED_ALL if self.tagged_vlans: @@ -60,17 +85,32 @@ def dot1q_mode(self) -> Optional[InterfaceDot1qMode]: @property @abstractmethod def duplex(self) -> InterfaceDuplex: - """Determine the configured Duplex of the interface.""" + """Determine the configured Duplex of the interface. + + Returns: + InterfaceDuplex: The configured duplex mode. + + """ @property @abstractmethod def enabled(self) -> bool: - """Determines if the interface is enabled.""" + """Determines if the interface is enabled. + + Returns: + bool: True if the interface is enabled, else False. + + """ @property @abstractmethod def has_nac(self) -> bool: - """Determine if the interface has NAC configured.""" + """Determine if the interface has NAC (Network Access Control) configured. + + Returns: + bool: True if the interface has NAC configured, else False. + + """ @property def ipv4_interface(self) -> Optional[IPv4Interface]: @@ -85,120 +125,244 @@ def ipv4_interfaces(self) -> Iterable[IPv4Interface]: @property @abstractmethod def is_bundle(self) -> bool: - """Determine if the interface is a bundle.""" + """Determine if the interface is a bundle. + + Returns: + bool: True if the interface is a bundle, else False. + + """ @property @abstractmethod def is_loopback(self) -> bool: - """Determine if the interface is a loopback.""" + """Determine if the interface is a loopback. + + Returns: + bool: True if the interface is a loopback, else False. + + """ @property def is_subinterface(self) -> bool: - """Determine if the interface is a subinterface.""" + """Determine if the interface is a subinterface. + + Returns: + bool: True if the interface is a subinterface, else False. + + """ return "." in self.name @property @abstractmethod def is_svi(self) -> bool: - """Determine if the interface is an SVI.""" + """Determine if the interface is an SVI. + + Returns: + bool: True if the interface is an SVI, else False. + + """ @property @abstractmethod def module_number(self) -> Optional[int]: - """Determine the module number of the interface.""" + """Determine the module number of the interface. + + Returns: + Optional[int]: The module number of the interface or None. + + """ @property @abstractmethod def nac_control_direction_in(self) -> bool: - """Determine if the interface has NAC 'control direction in' configured.""" + """Determine if the interface has NAC 'control direction in' configured. + + Returns: + bool: True if 'control direction in' is configured, else False. + + """ @property @abstractmethod def nac_host_mode(self) -> Optional[NACHostMode]: - """Determine the NAC host mode.""" + """Determine the NAC host mode. + + Returns: + Optional[NACHostMode]: The NAC host mode or None. + + """ @property @abstractmethod def nac_mab_first(self) -> bool: - """Determine if the interface has NAC configured for MAB first.""" + """Determine if the interface has NAC configured for MAB first. + + Returns: + bool: True is MAB authentication is configured first, else False. + + """ @property @abstractmethod def nac_max_dot1x_clients(self) -> int: - """Determine the max dot1x clients.""" + """Determine the max dot1x clients. + + Returns: + int: The max amount of dot1x clients. + + """ @property @abstractmethod def nac_max_mab_clients(self) -> int: - """Determine the max mab clients.""" + """Determine the max mab clients. + + Returns: + int: The max number of clients that can be authenticated using MAB. + + """ @property @abstractmethod def name(self) -> str: - """Determine the name of the interface.""" + """Determine the name of the interface. + + Returns: + str: The interface name. + + """ @property @abstractmethod def native_vlan(self) -> Optional[int]: - """Determine the native VLAN.""" + """Determine the native VLAN. + + Returns: + Optional[int]: Native VLAN ID or None if not set. + + """ @property @abstractmethod def number(self) -> str: - """Remove letters from the interface name, leaving just numbers and symbols.""" + """Remove letters from the interface name, leaving just numbers and symbols. + + Returns: + str: The interface number. + + """ @property @abstractmethod def parent_name(self) -> Optional[str]: - """Determine the parent bundle interface name.""" + """Determine the parent bundle interface name. + + Returns: + Optional[str]: The logical parent bundle interface name or None. + + """ @property @abstractmethod def poe(self) -> bool: - """Determine if PoE is enabled.""" + """Determine if PoE is enabled. + + Returns: + bool: True if PoE is enabled, else False. + + """ @property @abstractmethod def port_number(self) -> int: - """Determine the interface port number.""" + """Determine the interface port number. + + Returns: + int: The interface port number. + + """ @property @abstractmethod def speed(self) -> Optional[tuple[int, ...]]: - """Determine the statically allowed speeds the interface can operate at. In Mbps.""" + """Determine the statically allowed speeds the interface can operate at. In Mbps. + + Returns: + Optional[tuple[int, ...]]: The allowed speeds or None. + + """ @property @abstractmethod def subinterface_number(self) -> Optional[int]: - """Determine the sub-interface number.""" + """Determine the sub-interface number. + + Returns: + Optional[int]: The sub-interface number or None. + + """ @property @abstractmethod def tagged_all(self) -> bool: - """Determine if all the VLANs are tagged.""" + """Determine if all the VLANs are tagged. + + Returns: + bool: True if all the VLANs are tagged, else False. + + """ @property @abstractmethod def tagged_vlans(self) -> tuple[int, ...]: - """Determine the tagged VLANs.""" + """Determine the tagged VLANs. + + Returns: + tuple[int, ...]: Tuple of tagged VLAN IDs. + + """ @property @abstractmethod def vrf(self) -> str: - """Determine the VRF.""" + """Determine the VRF. + + Returns: + str: The VRF name. + + """ @property @abstractmethod def _bundle_prefix(self) -> str: - pass + """Determine the bundle prefix name. + + Returns: + str: The bundle prefix name. + + """ class HConfigViewBase(ABC): + """Abstract class to view HConfig config tree objects.""" + def __init__(self, config: HConfig) -> None: + """Initialize the HConfig view base class. + + Args: + config (HConfig): The HConfig object to view. + + """ self.config: HConfig = config @property def bundle_interface_views(self) -> Iterable[ConfigViewInterfaceBase]: + """Determine the bundle interface views. + + Yields: + Iterable[ConfigViewInterfaceBase]: Iterable of bundle interface views. + + """ for interface_view in self.interface_views: if interface_view.is_bundle: yield interface_view @@ -211,19 +375,48 @@ def dot1q_mode_from_vlans( *, tagged_all: bool = False, ) -> Optional[InterfaceDot1qMode]: - pass + """Determine the 802.1Q mode from VLANs. + + Args: + untagged_vlan (Optional[int], optional): Untagged VLAN. Defaults to None. + tagged_vlans (tuple[int, ...], optional): Tagged VLANs. Defaults to (). + tagged_all (bool, optional): Tagged all VLANs. Defaults to False. + + Returns: + Optional[InterfaceDot1qMode]: The 802.1Q mode or None if not set. + + """ @property @abstractmethod def hostname(self) -> Optional[str]: - pass + """Determine the hostname. + + Returns: + Optional[str]: The hostname or None. + + """ @property @abstractmethod def interface_names_mentioned(self) -> frozenset[str]: - """Returns a set with all the interface names mentioned in the config.""" + """Returns a set with all the interface names mentioned in the config. + + Returns: + frozenset[str]: All the interface names mentioned in the config. + + """ def interface_view_by_name(self, name: str) -> Optional[ConfigViewInterfaceBase]: + """Determine the interface view by name. + + Args: + name (str): The interface name. + + Returns: + Optional[ConfigViewInterfaceBase]: The interface view or None. + + """ for interface_view in self.interface_views: if interface_view.name == name: return interface_view @@ -232,30 +425,63 @@ def interface_view_by_name(self, name: str) -> Optional[ConfigViewInterfaceBase] @property @abstractmethod def interface_views(self) -> Iterable[ConfigViewInterfaceBase]: - pass + """Determine the configured interfaces. + + Returns: + Iterable[ConfigViewInterfaceBase]: The configured interfaces. + + """ @property @abstractmethod def interfaces(self) -> Iterable[HConfigChild]: - pass + """Determine the configured interfaces. + + Returns: + Iterable[HConfigChild]: An iterbale of the configured interfaces' + HConfig objects. + + """ @property def interfaces_names(self) -> Iterable[str]: + """Determine the configured interface names. + + Yields: + Iterator[Iterable[str]]: The configured interface names. + + """ for interface_view in self.interface_views: yield interface_view.name @property @abstractmethod def ipv4_default_gw(self) -> Optional[IPv4Address]: - pass + """Determine the IPv4 default gateway IPv4Address. + + Returns: + Optional[IPv4Address]: The IPv4 default gateway object or None. + + """ @property @abstractmethod def location(self) -> str: - pass + """Determine the location of the device. + + Returns: + str: Location name of the device. + + """ @property def module_numbers(self) -> Iterable[int]: + """Determine the configured module numbers. + + Yields: + Iterator[Iterable[int]]: Yields unique module numbers. + + """ seen: set[int] = set() for interface_view in self.interface_views: if module_number := interface_view.module_number: @@ -267,14 +493,29 @@ def module_numbers(self) -> Iterable[int]: @property @abstractmethod def stack_members(self) -> Iterable[StackMember]: - """Determine the configured stack members.""" + """Determine the configured stack members. + + Returns: + Iterable[StackMember]: The configured stack members. + + """ @property def vlan_ids(self) -> frozenset[int]: - """Determine the VLAN IDs.""" + """Determine the VLAN IDs. + + Returns: + frozenset[int]: The VLAN IDs. + + """ return frozenset(vlan.id for vlan in self.vlans) @property @abstractmethod def vlans(self) -> Iterable[Vlan]: - """Determine the configured VLANs.""" + """Determine the configured VLANs. + + Returns: + Iterable[Vlan]: The configured VLANs. + + """ From 9a422b3c70ccd30e72d0965f1368ffe68ba4b185 Mon Sep 17 00:00:00 2001 From: Carlos Huaccho Date: Wed, 16 Apr 2025 15:16:56 -0400 Subject: [PATCH 3/7] added HConfigDriverBase documentation --- .gitignore | 1 + ...ome%huacc%hier_config%hier_config%child.py | Bin 0 -> 75545 bytes hier_config/child.py | 155 +++++++++++++----- hier_config/children.py | 50 +++++- hier_config/models.py | 96 +++++++++++ hier_config/platforms/driver_base.py | 66 +++++++- poetry.lock | 88 ++++++++-- 7 files changed, 394 insertions(+), 62 deletions(-) create mode 100644 .undodir/%home%huacc%hier_config%hier_config%child.py diff --git a/.gitignore b/.gitignore index ac53476d..6a9617f5 100644 --- a/.gitignore +++ b/.gitignore @@ -109,3 +109,4 @@ venv.bak/ # vscode .vscode/ +.qodo diff --git a/.undodir/%home%huacc%hier_config%hier_config%child.py b/.undodir/%home%huacc%hier_config%hier_config%child.py new file mode 100644 index 0000000000000000000000000000000000000000..a955245aa97f7e28c44d27b62b202fab2a2812b1 GIT binary patch literal 75545 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 None: + """Initialize the HconfigChild class. + + Args: + parent: Either an Hconfig object of a parent HConfigChild object. + text: The text config line of the HConfig or HconfigChild object. + + """ super().__init__() - self.parent = parent + self.parent: HConfig | HConfigChild = parent self._text: str = text.strip() self.real_indent_level: int # 0 is the default. Positive weights sink while negative weights rise. @@ -81,34 +89,70 @@ def __eq__(self, other: object) -> bool: return self.children == other.children def __ne__(self, other: object) -> bool: - return not self.__eq__(other) + return not self.__eq__(other=other) @property def driver(self) -> HConfigDriverBase: + """Returns the driver of the HConfig object at the base of the tree. + + Returns: + HConfigDriverBase: The driver of the HConfig object at the base of the tree + + """ return self.root.driver @property def text(self) -> str: + """The text config of the HConfigChild object. + + Returns: + str: Config text for the HConfigChild. + + """ return self._text @text.setter def text(self, value: str) -> None: """Used for when self.text is changed after the object is instantiated to rebuild the children dictionary. + + Args: + value (str): Config text for the HConfigChild. + """ self._text = value.strip() self.parent.children.rebuild_mapping() @property def text_without_negation(self) -> str: + """The text config of the HConfigChild object without negation. + + Returns: + str: Config text for the HConfigChild without negation. + + """ return self.text.removeprefix(self.driver.negation_prefix) @property def root(self) -> HConfig: - """Returns the HConfig object at the base of the tree.""" + """Returns the HConfig object at the base of the tree. + + Returns: + HConfig: The HConfig object at the base of the tree. + + """ return self.parent.root def lines(self, *, sectional_exiting: bool = False) -> Iterable[str]: + """Returns the config lines of the HConfigChild object. + + Args: + sectional_exiting (bool, optional): Exit sectionally. + + Yields: + Iterator[Iterable[str]]: cisco_style_text string. + + """ yield self.cisco_style_text() for child in sorted(self.children): yield from child.lines(sectional_exiting=sectional_exiting) @@ -119,7 +163,7 @@ def lines(self, *, sectional_exiting: bool = False) -> Iterable[str]: @property def sectional_exit(self) -> Optional[str]: for rule in self.driver.rules.sectional_exiting: - if self.is_lineage_match(rule.match_rules): + if self.is_lineage_match(rules=rule.match_rules): if exit_text := rule.exit_text: return exit_text return None @@ -131,7 +175,7 @@ def sectional_exit(self) -> Optional[str]: def delete_sectional_exit(self) -> None: try: - potential_exit = self.children[-1] + potential_exit: HConfigChild = self.children[-1] except IndexError: return @@ -139,7 +183,12 @@ def delete_sectional_exit(self) -> None: potential_exit.delete() def depth(self) -> int: - """Returns the distance to the root HConfig object i.e. indent level.""" + """Returns the distance to the root HConfig object i.e. indent level. + + Returns: + int: Number of indents from the root HConfig object. + + """ return self.parent.depth() + 1 def move(self, new_parent: Union[HConfig, HConfigChild]) -> None: @@ -157,7 +206,7 @@ def move(self, new_parent: Union[HConfig, HConfigChild]) -> None: :param new_parent: HConfigChild object -> type list """ - new_parent.children.append(self) + new_parent.children.append(child=self) self.delete() def lineage(self) -> Iterator[HConfigChild]: @@ -175,13 +224,22 @@ def cisco_style_text( style: str = "without_comments", tag: Optional[str] = None, ) -> str: - """Return a Cisco style formated line i.e. indentation_level + text ! comments.""" + """Yields a Cisco style formated line. + + Args: + style str: The style to use. Defaults to "without_comments". + tag Optional[str]: The tag to filter by. Defaults to None. + + Returns: + str: Indentation + text + comments if any. + + """ comments: list[str] = [] if style == "without_comments": pass elif style == "merged": # count the number of instances that have the tag - instance_count = 0 + instance_count: int = 0 instance_comments: set[str] = set() for instance in self.instances: if tag is None or tag in instance.tags: @@ -189,14 +247,14 @@ def cisco_style_text( instance_comments.update(instance.comments) # should the word 'instance' be plural? - word = "instance" if instance_count == 1 else "instances" + word: str = "instance" if instance_count == 1 else "instances" comments.append(f"{instance_count} {word}") comments.extend(instance_comments) elif style == "with_comments": comments.extend(self.comments) - comments_str = f" !{', '.join(sorted(comments))}" if comments else "" + comments_str: str = f" !{', '.join(sorted(comments))}" if comments else "" return f"{self.indentation}{self.text}{comments_str}" @property @@ -205,13 +263,13 @@ def indentation(self) -> str: def delete(self) -> None: """Delete the current object from its parent.""" - self.parent.children.delete(self) + self.parent.children.delete(child_or_text=self) def tags_add(self, tag: Union[str, Iterable[str]]) -> None: """Add a tag to self._tags on all leaf nodes.""" if self.is_branch: for child in self.children: - child.tags_add(tag) + child.tags_add(tag=tag) elif isinstance(tag, str): self._tags.add(tag) else: @@ -221,7 +279,7 @@ def tags_remove(self, tag: Union[str, Iterable[str]]) -> None: """Remove a tag from self._tags on all leaf nodes.""" if self.is_branch: for child in self.children: - child.tags_remove(tag) + child.tags_remove(tag=tag) elif isinstance(tag, str): self._tags.remove(tag) else: @@ -229,18 +287,18 @@ def tags_remove(self, tag: Union[str, Iterable[str]]) -> None: def negate(self) -> HConfigChild: """Negate self.text.""" - if negate_with := self.driver.negate_with(self): + if negate_with := self.driver.negate_with(config=self): self.text = negate_with return self - if self.use_default_for_negation(self): + if self.use_default_for_negation(config=self): return self._default() - return self.driver.swap_negation(self) + return self.driver.swap_negation(child=self) def use_default_for_negation(self, config: HConfigChild) -> bool: return any( - config.is_lineage_match(rule.match_rules) + config.is_lineage_match(rules=rule.match_rules) for rule in self.driver.rules.negation_default_when ) @@ -278,25 +336,30 @@ def is_idempotent_command(self, other_children: Iterable[HConfigChild]) -> bool: """Determine if self.text is an idempotent change.""" # Avoid list commands from matching as idempotent for rule in self.driver.rules.idempotent_commands_avoid: - if self.is_lineage_match(rule.match_rules): + if self.is_lineage_match(rules=rule.match_rules): return False # Idempotent command identification - return bool(self.driver.idempotent_for(self, other_children)) + return bool( + self.driver.idempotent_for( + config=self, + other_children=other_children, + ) + ) def use_sectional_overwrite_without_negation(self) -> bool: """Check self's text to see if negation should be handled by overwriting the section without first negating it. """ return any( - self.is_lineage_match(rule.match_rules) + self.is_lineage_match(rules=rule.match_rules) for rule in self.driver.rules.sectional_overwrite_no_negate ) def use_sectional_overwrite(self) -> bool: """Determines if self.text matches a sectional overwrite rule.""" return any( - self.is_lineage_match(rule.match_rules) + self.is_lineage_match(rules=rule.match_rules) for rule in self.driver.rules.sectional_overwrite ) @@ -310,18 +373,20 @@ def overwrite_with( """Deletes delta.child[self.text], adds a deep copy of target to delta.""" if self.children != target.children: if negate: - if negated := delta.children.get(self.text): + if negated := delta.children.get(key=self.text): negated.negate() else: - negated = delta.add_child( - self.text, check_if_present=False + negated: HConfigChild = delta.add_child( + text=self.text, check_if_present=False ).negate() negated.comments.add("dropping section") else: - delta.children.delete(self.text) + delta.children.delete(child_or_text=self.text) if self.children: - new_item = delta.add_deep_copy_of(target) + new_item: HConfigChild = delta.add_deep_copy_of( + child_to_add=target, + ) new_item.comments.add("re-create section") def line_inclusion_test( @@ -356,21 +421,26 @@ def all_children_sorted_by_tags( ) -> Iterator[HConfigChild]: """Yield all children recursively that match include/exclude tags.""" if self.is_leaf: - if self.line_inclusion_test(include_tags, exclude_tags): + if self.line_inclusion_test( + include_tags=include_tags, + exclude_tags=exclude_tags, + ): yield self else: - self_iter = iter((self,)) + self_iter: Iterator[HConfigChild] = iter((self,)) for child in sorted(self.children): - included_children = child.all_children_sorted_by_tags( - include_tags, - exclude_tags, + included_children: Iterator[HConfigChild] = ( + child.all_children_sorted_by_tags( + include_tags=include_tags, + exclude_tags=exclude_tags, + ) ) if peek := next(included_children, None): yield from chain(self_iter, (peek,), included_children) def is_lineage_match(self, rules: tuple[MatchRule, ...]) -> bool: """A generic test against a lineage of HConfigChild objects.""" - lineage = tuple(self.lineage()) + lineage: tuple[HConfigBase, ...] = tuple(self.lineage()) return len(rules) == len(lineage) and all( child.is_match( @@ -414,7 +484,10 @@ def is_match( # noqa: PLR0911 return False # Regex filter - if isinstance(re_search, str) and not search(re_search, self.text): + if isinstance(re_search, str) and not search( + pattern=re_search, + string=self.text, + ): return False # The below filters are less commonly used @@ -436,9 +509,9 @@ def is_match( # noqa: PLR0911 def add_children_deep(self, lines: Iterable[str]) -> HConfigChild: """Add child instances of HConfigChild deeply.""" - base = self + base: HConfigChild = self for line in lines: - base = base.add_child(line) + base = base.add_child(text=line) return base def _default(self) -> HConfigChild: @@ -447,11 +520,11 @@ def _default(self) -> HConfigChild: return self def instantiate_child(self, text: str) -> HConfigChild: - return HConfigChild(self, text) + return HConfigChild(parent=self, text=text) def _is_duplicate_child_allowed(self) -> bool: """Determine if duplicate(identical text) children are allowed under the parent.""" return any( - self.is_lineage_match(rule.match_rules) + self.is_lineage_match(rules=rule.match_rules) for rule in self.driver.rules.parent_allows_duplicate_child ) diff --git a/hier_config/children.py b/hier_config/children.py index 3707b924..fcbc2685 100644 --- a/hier_config/children.py +++ b/hier_config/children.py @@ -12,6 +12,7 @@ class HConfigChildren: def __init__(self) -> None: + """Initialize the HConfigChildren class.""" self._data: list[HConfigChild] = [] self._mapping: dict[str, HConfigChild] = {} @@ -81,6 +82,17 @@ def append( *, update_mapping: bool = True, ) -> HConfigChild: + """Add a child instance of HConfigChild. + + Args: + child (HConfigChild): The child to add. + update_mapping (bool, optional): Whether to update the text to child mapping. + Defaults to True. + + Returns: + HConfigChild: The child that was added. + + """ self._data.append(child) if update_mapping: self._mapping.setdefault(child.text, child) @@ -93,19 +105,29 @@ def clear(self) -> None: self._mapping.clear() def delete(self, child_or_text: Union[HConfigChild, str]) -> None: - """Delete a child from self._data and self._mapping.""" + """Delete a child from self._data and self._mapping. + + Args: + child_or_text (Union[HConfigChild, str]): The child or text to delete. + + """ if isinstance(child_or_text, str): if child_or_text in self._mapping: self._data[:] = [c for c in self._data if c.text != child_or_text] self.rebuild_mapping() else: - old_len = len(self._data) + old_len: int = len(self._data) self._data = [c for c in self._data if c is not child_or_text] if old_len != len(self._data): self.rebuild_mapping() def extend(self, children: Iterable[HConfigChild]) -> None: - """Add child instances of HConfigChild.""" + """Add child instances of HConfigChild and update _mapping. + + Args: + children (Iterable[HConfigChild]): The children to add. + + """ self._data.extend(children) for child in children: self._mapping.setdefault(child.text, child) @@ -113,13 +135,33 @@ def extend(self, children: Iterable[HConfigChild]) -> None: def get( self, key: str, default: Optional[_D] = None ) -> Union[HConfigChild, _D, None]: + """Get a child from self._mapping. + + Args: + key (str): The config text to get. + default (Optional[_D], optional): Object to return if the key doesn't exist. + Defaults to None. + + Returns: + Union[HConfigChild, _D, None]: HConfigChild if found, default otherwise. + + """ return self._mapping.get(key, default) def index(self, child: HConfigChild) -> int: + """Get the index of a child in self._data. + + Args: + child (HConfigChild): The child to get the index of. + + Returns: + int: The index of the child. + + """ return self._data.index(child) def rebuild_mapping(self) -> None: - """Rebuild self._mapping.""" + """Rebuild self._mapping from self._data.""" self._mapping.clear() for child in self._data: self._mapping.setdefault(child.text, child) diff --git a/hier_config/models.py b/hier_config/models.py index fe1fd229..e42ff696 100644 --- a/hier_config/models.py +++ b/hier_config/models.py @@ -20,6 +20,16 @@ class DumpLine(BaseModel): class MatchRule(BaseModel): + """A rule for matching configuration lines. + + Attrs: + equals: A string or set of strings to match exactly. + startswith: A string or tuple of strings to match lines that start with. + endswith: A string or tuple of strings to match lines that end with. + contains: A string or tuple of strings to match lines that contain. + re_search: A regex pattern to match lines that match the pattern. + """ + equals: Union[str, frozenset[str], None] = None startswith: Union[str, tuple[str, ...], None] = None endswith: Union[str, tuple[str, ...], None] = None @@ -33,61 +43,147 @@ class TagRule(BaseModel): class SectionalExitingRule(BaseModel): + """A rule for exiting a section. + + Attrs: + match_rules: A tuple of rules that must match to exit the section. + exit_text: The text returned when exiting the section. + """ + match_rules: tuple[MatchRule, ...] exit_text: str class SectionalOverwriteRule(BaseModel): + """Represents a rule for overwriting configuration lines. + + Attrs: + match_rules: A tuple of rules that must match to overwrite the section. + """ + match_rules: tuple[MatchRule, ...] class SectionalOverwriteNoNegateRule(BaseModel): + """Represents a rule for overwriting configuration lines. + + Attrs: + match_rules: A tuple of rules that must match to overwrite the section. + """ + match_rules: tuple[MatchRule, ...] class OrderingRule(BaseModel): + """Represents a rule for ordering configuration lines. + + Attrs: + match_rules: A tuple of rules that must match to be ordered. + weight: An integer determining the order (lower weights are processed earlier). + """ + match_rules: tuple[MatchRule, ...] weight: int class IndentAdjustRule(BaseModel): + """Represents a rule for adjusting indentation. + + Attrs: + start_expression: Regex or text marking the start of an adjustment. + end_expression: Regex or text marking the end of an adjustment. + """ + start_expression: str end_expression: str class ParentAllowsDuplicateChildRule(BaseModel): + """Represents a rule for allowing duplicate child configurations. + + Attrs: + match_rules: A tuple of rules that must match to allow duplicate children. + """ + match_rules: tuple[MatchRule, ...] class FullTextSubRule(BaseModel): + """Represents a full-text substitution rule. + + Attrs: + search: The text to search for. + replace: The text to replace the search text with. + """ + search: str replace: str class PerLineSubRule(BaseModel): + """Represents a per-line substitution rule. + + Attrs: + search: The text to search for. + replace: The text to replace the search text with. + """ + search: str replace: str class IdempotentCommandsRule(BaseModel): + """Represents a rule for idempotent commands. + + Attrs: + match_rules: A tuple of rules that must match to be idempotent. + """ + match_rules: tuple[MatchRule, ...] class IdempotentCommandsAvoidRule(BaseModel): + """Represents a rule for avoiding idempotent commands. + + Attrs: + match_rules: A tuple of rules that must match to avoid idempotent commands. + """ + match_rules: tuple[MatchRule, ...] class Instance(BaseModel): + """Represents a single configuration entity within a HConfig model. + + Attrs: + id: A unique identifier for the instance. + comments: A set of comments associated with the instance. + tags: A set of tags associated with the instance. + """ + id: PositiveInt comments: frozenset[str] tags: frozenset[str] class NegationDefaultWhenRule(BaseModel): + """Represents a rule for matching configuration lines. + + Attrs: + match_rules: A tuple of rules that must match to negate the default behavior. + """ + match_rules: tuple[MatchRule, ...] class NegationDefaultWithRule(BaseModel): + """Represents a rule for matching and negating configuration lines. + + Attrs: + match_rules: A tuple of rules that must match to negate the default behavior. + use: The default behavior to negate. + """ + match_rules: tuple[MatchRule, ...] use: str diff --git a/hier_config/platforms/driver_base.py b/hier_config/platforms/driver_base.py index 12ba5a9e..fc61c123 100644 --- a/hier_config/platforms/driver_base.py +++ b/hier_config/platforms/driver_base.py @@ -52,28 +52,56 @@ class HConfigDriverBase(ABC): """ def __init__(self) -> None: - self.rules = self._instantiate_rules() + """Initialize the HConfigDriverBase class.""" + self.rules: HConfigDriverRules = self._instantiate_rules() def idempotent_for( self, config: HConfigChild, other_children: Iterable[HConfigChild], ) -> Optional[HConfigChild]: + """Determine if `config` is an idempotent change. + + Args: + config (HConfigChild): HConfigChild to check. + other_children (Iterable[HConfigChild]): Other HConfigChildren to check. + + Returns: + Optional[HConfigChild]: HConfigChild that matches `config` or None. + + """ for rule in self.rules.idempotent_commands: - if config.is_lineage_match(rule.match_rules): + if config.is_lineage_match(rules=rule.match_rules): for other_child in other_children: - if other_child.is_lineage_match(rule.match_rules): + if other_child.is_lineage_match(rules=rule.match_rules): return other_child return None def negate_with(self, config: HConfigChild) -> Optional[str]: + """Determine if `config` should be negated. + + Args: + config (HConfigChild): HConfigChild to check. + + Returns: + Optional[str]: String to use for negation or None. + + """ for with_rule in self.rules.negate_with: - if config.is_lineage_match(with_rule.match_rules): + if config.is_lineage_match(rules=with_rule.match_rules): return with_rule.use return None def swap_negation(self, child: HConfigChild) -> HConfigChild: - """Swap negation of a `child.text`.""" + """Swap negation of a `child.text`. + + Args: + child (HConfigChild): The child to swap negation. + + Returns: + HConfigChild: The child with negation swapped. + + """ if child.text.startswith(self.negation_prefix): child.text = child.text_without_negation else: @@ -83,17 +111,43 @@ def swap_negation(self, child: HConfigChild) -> HConfigChild: @property def declaration_prefix(self) -> str: + """The prefix for declarations. + + Returns: + str: The declaration string. + + """ return "" @property def negation_prefix(self) -> str: + """The prefix for negation. + + Returns: + str: The negation string. + + """ return "no " @staticmethod def config_preprocessor(config_text: str) -> str: + """Preprocess the config text. + + Args: + config_text (str): The config text. + + Returns: + str: The preprocessed config text. + + """ return config_text @staticmethod @abstractmethod def _instantiate_rules() -> HConfigDriverRules: - pass + """Abstract method to instantiate rules. + + Returns: + HConfigDriverRules: The rules. + + """ diff --git a/poetry.lock b/poetry.lock index 07b0f42e..74a856a2 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.2 and should not be changed by hand. [[package]] name = "annotated-types" @@ -6,6 +6,7 @@ version = "0.7.0" description = "Reusable constraint types to use with typing.Annotated" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, @@ -17,6 +18,7 @@ version = "0.8.1" description = "Read/rewrite/write Python ASTs" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" +groups = ["dev"] files = [ {file = "astor-0.8.1-py2.py3-none-any.whl", hash = "sha256:070a54e890cefb5b3739d19f30f5a5ec840ffc9c50ffa7d23cc9fc1a38ebbfc5"}, {file = "astor-0.8.1.tar.gz", hash = "sha256:6a6effda93f4e1ce9f618779b2dd1d9d84f1e32812c23a29b3fff6fd7f63fa5e"}, @@ -28,6 +30,7 @@ version = "3.3.8" description = "An abstract syntax tree for Python with inference support." optional = false python-versions = ">=3.9.0" +groups = ["dev"] files = [ {file = "astroid-3.3.8-py3-none-any.whl", hash = "sha256:187ccc0c248bfbba564826c26f070494f7bc964fd286b6d9fff4420e55de828c"}, {file = "astroid-3.3.8.tar.gz", hash = "sha256:a88c7994f914a4ea8572fac479459f4955eeccc877be3f2d959a33273b0cf40b"}, @@ -42,6 +45,7 @@ version = "2.5.post1" description = "Bash style brace expander." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "bracex-2.5.post1-py3-none-any.whl", hash = "sha256:13e5732fec27828d6af308628285ad358047cec36801598368cb28bc631dbaf6"}, {file = "bracex-2.5.post1.tar.gz", hash = "sha256:12c50952415bfa773d2d9ccb8e79651b8cdb1f31a42f6091b804f6ba2b4a66b6"}, @@ -53,6 +57,7 @@ version = "8.1.8" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, @@ -67,6 +72,8 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["dev"] +markers = "platform_system == \"Windows\" or sys_platform == \"win32\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -78,6 +85,7 @@ version = "7.6.10" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "coverage-7.6.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5c912978f7fbf47ef99cec50c4401340436d200d41d714c7a4766f377c5b7b78"}, {file = "coverage-7.6.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a01ec4af7dfeb96ff0078ad9a48810bb0cc8abcb0115180c6013a6b26237626c"}, @@ -147,7 +155,7 @@ files = [ tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} [package.extras] -toml = ["tomli"] +toml = ["tomli ; python_full_version <= \"3.11.0a6\""] [[package]] name = "dill" @@ -155,6 +163,7 @@ version = "0.3.9" description = "serialize all of Python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "dill-0.3.9-py3-none-any.whl", hash = "sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a"}, {file = "dill-0.3.9.tar.gz", hash = "sha256:81aa267dddf68cbfe8029c42ca9ec6a4ab3b22371d1c450abc54422577b4512c"}, @@ -170,6 +179,8 @@ version = "1.2.2" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" +groups = ["dev"] +markers = "python_version < \"3.11\"" files = [ {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, @@ -184,6 +195,7 @@ version = "2.1.1" description = "execnet: rapid multi-Python deployment" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc"}, {file = "execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"}, @@ -198,6 +210,7 @@ version = "1.0.1" description = "CLI tool to convert a python project's %-formatted strings to f-strings." optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "flynt-1.0.1-py3-none-any.whl", hash = "sha256:65d1c546434827275123222a98408e9561bcd67db832dd58f530ff17b8329ec1"}, {file = "flynt-1.0.1.tar.gz", hash = "sha256:988aac00672a5469726cc0a17cef7d1178c284a9fe8563458db2475d0aaed965"}, @@ -216,6 +229,7 @@ version = "2.1.0" description = "Copy your docs directly to the gh-pages branch." optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, @@ -233,6 +247,7 @@ version = "2024.6.6" description = "Generate a dot graph from the output of several profilers." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "gprof2dot-2024.6.6-py2.py3-none-any.whl", hash = "sha256:45b14ad7ce64e299c8f526881007b9eb2c6b75505d5613e96e66ee4d5ab33696"}, {file = "gprof2dot-2024.6.6.tar.gz", hash = "sha256:fa1420c60025a9eb7734f65225b4da02a10fc6dd741b37fa129bc6b41951e5ab"}, @@ -244,6 +259,8 @@ version = "8.5.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "python_version < \"3.10\"" files = [ {file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"}, {file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"}, @@ -253,12 +270,12 @@ files = [ zipp = ">=3.20" [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] perf = ["ipython"] -test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +test = ["flufl.flake8", "importlib-resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] type = ["pytest-mypy"] [[package]] @@ -267,6 +284,7 @@ version = "2.0.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, @@ -278,6 +296,7 @@ version = "5.13.2" description = "A Python utility / library to sort Python imports." optional = false python-versions = ">=3.8.0" +groups = ["dev"] files = [ {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, @@ -292,6 +311,7 @@ version = "3.1.5" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb"}, {file = "jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb"}, @@ -309,6 +329,7 @@ version = "3.7" description = "Python implementation of John Gruber's Markdown." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803"}, {file = "markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2"}, @@ -327,6 +348,7 @@ version = "3.0.0" description = "Python port of markdown-it. Markdown parsing, done right!" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, @@ -351,6 +373,7 @@ version = "3.0.2" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, @@ -421,6 +444,7 @@ version = "0.7.0" description = "McCabe checker, plugin for flake8" optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, @@ -432,6 +456,7 @@ version = "0.1.2" description = "Markdown URL utilities" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, @@ -443,6 +468,7 @@ version = "1.3.4" description = "A deep merge function for 🐍." optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, @@ -454,6 +480,7 @@ version = "1.6.1" description = "Project documentation with Markdown." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e"}, {file = "mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2"}, @@ -477,7 +504,7 @@ watchdog = ">=2.0" [package.extras] i18n = ["babel (>=2.9.0)"] -min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-import (==1.0)", "importlib-metadata (==4.4)", "jinja2 (==2.11.1)", "markdown (==3.3.6)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "mkdocs-get-deps (==0.2.0)", "packaging (==20.5)", "pathspec (==0.11.1)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "watchdog (==2.0)"] +min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4) ; platform_system == \"Windows\"", "ghp-import (==1.0)", "importlib-metadata (==4.4) ; python_version < \"3.10\"", "jinja2 (==2.11.1)", "markdown (==3.3.6)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "mkdocs-get-deps (==0.2.0)", "packaging (==20.5)", "pathspec (==0.11.1)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "watchdog (==2.0)"] [[package]] name = "mkdocs-get-deps" @@ -485,6 +512,7 @@ version = "0.2.0" description = "MkDocs extension that lists all dependencies according to a mkdocs.yml file" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134"}, {file = "mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c"}, @@ -502,6 +530,7 @@ version = "7.1.2" description = "Mkdocs Markdown includer plugin." optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "mkdocs_include_markdown_plugin-7.1.2-py3-none-any.whl", hash = "sha256:ff1175d1b4f83dea6a38e200d6f0c3db10308975bf60c197d31172671753dbc4"}, {file = "mkdocs_include_markdown_plugin-7.1.2.tar.gz", hash = "sha256:1b393157b1aa231b0e6c59ba80f52b723f4b7827bb7a1264b505334f8542aaf1"}, @@ -520,6 +549,7 @@ version = "1.14.1" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "mypy-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:52686e37cf13d559f668aa398dd7ddf1f92c5d613e4f8cb262be2fb4fedb0fcb"}, {file = "mypy-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1fb545ca340537d4b45d3eecdb3def05e913299ca72c290326be19b3804b39c0"}, @@ -579,6 +609,7 @@ version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.5" +groups = ["dev"] 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"}, @@ -590,6 +621,7 @@ version = "1.9.1" description = "Node.js virtual environment builder" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["dev"] files = [ {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, @@ -601,6 +633,7 @@ version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, @@ -612,6 +645,7 @@ version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, @@ -623,6 +657,7 @@ version = "4.3.6" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, @@ -639,6 +674,7 @@ version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, @@ -654,6 +690,7 @@ version = "2.10.5" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "pydantic-2.10.5-py3-none-any.whl", hash = "sha256:4dd4e322dbe55472cb7ca7e73f4b63574eecccf2835ffa2af9021ce113c83c53"}, {file = "pydantic-2.10.5.tar.gz", hash = "sha256:278b38dbbaec562011d659ee05f63346951b3a248a6f3642e1bc68894ea2b4ff"}, @@ -666,7 +703,7 @@ typing-extensions = ">=4.12.2" [package.extras] email = ["email-validator (>=2.0.0)"] -timezone = ["tzdata"] +timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""] [[package]] name = "pydantic-core" @@ -674,6 +711,7 @@ version = "2.27.2" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"}, {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"}, @@ -786,6 +824,7 @@ version = "2.19.1" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, @@ -800,6 +839,7 @@ version = "3.3.3" description = "python code static checker" optional = false python-versions = ">=3.9.0" +groups = ["dev"] files = [ {file = "pylint-3.3.3-py3-none-any.whl", hash = "sha256:26e271a2bc8bce0fc23833805a9076dd9b4d5194e2a02164942cb3cdc37b4183"}, {file = "pylint-3.3.3.tar.gz", hash = "sha256:07c607523b17e6d16e2ae0d7ef59602e332caa762af64203c24b41c27139f36a"}, @@ -811,7 +851,7 @@ colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} dill = [ {version = ">=0.2", markers = "python_version < \"3.11\""}, {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, - {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, + {version = ">=0.3.6", markers = "python_version == \"3.11\""}, ] isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" mccabe = ">=0.6,<0.8" @@ -830,6 +870,7 @@ version = "0.8.2" description = "Utilities and helpers for writing Pylint plugins" optional = false python-versions = ">=3.7,<4.0" +groups = ["dev"] files = [ {file = "pylint_plugin_utils-0.8.2-py3-none-any.whl", hash = "sha256:ae11664737aa2effbf26f973a9e0b6779ab7106ec0adc5fe104b0907ca04e507"}, {file = "pylint_plugin_utils-0.8.2.tar.gz", hash = "sha256:d3cebf68a38ba3fba23a873809155562571386d4c1b03e5b4c4cc26c3eee93e4"}, @@ -844,6 +885,7 @@ version = "0.3.5" description = "A Pylint plugin to help Pylint understand the Pydantic" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pylint_pydantic-0.3.5-py3-none-any.whl", hash = "sha256:e7a54f09843b000676633ed02d5985a4a61c8da2560a3b0d46082d2ff171c4a1"}, ] @@ -859,6 +901,7 @@ version = "1.1.391" description = "Command line wrapper for pyright" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "pyright-1.1.391-py3-none-any.whl", hash = "sha256:54fa186f8b3e8a55a44ebfa842636635688670c6896dcf6cf4a7fc75062f4d15"}, {file = "pyright-1.1.391.tar.gz", hash = "sha256:66b2d42cdf5c3cbab05f2f4b76e8bec8aa78e679bfa0b6ad7b923d9e027cadb2"}, @@ -879,6 +922,7 @@ version = "8.3.4" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, @@ -901,6 +945,7 @@ version = "6.0.0" description = "Pytest plugin for measuring coverage." optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0"}, {file = "pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35"}, @@ -919,6 +964,7 @@ version = "1.8.1" description = "Profiling plugin for py.test" optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "pytest-profiling-1.8.1.tar.gz", hash = "sha256:3f171fa69d5c82fa9aab76d66abd5f59da69135c37d6ae5bf7557f1b154cb08d"}, {file = "pytest_profiling-1.8.1-py3-none-any.whl", hash = "sha256:3dd8713a96298b42d83de8f5951df3ada3e61b3e5d2a06956684175529e17aea"}, @@ -935,6 +981,7 @@ version = "6.0.1" description = "Invoke py.test as distutils command with dependency resolution" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "pytest-runner-6.0.1.tar.gz", hash = "sha256:70d4739585a7008f37bf4933c013fdb327b8878a5a69fcbb3316c88882f0f49b"}, {file = "pytest_runner-6.0.1-py3-none-any.whl", hash = "sha256:ea326ed6f6613992746062362efab70212089a4209c08d67177b3df1c52cd9f2"}, @@ -942,7 +989,7 @@ files = [ [package.extras] docs = ["jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx"] -testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-virtualenv", "types-setuptools"] +testing = ["pytest (>=6)", "pytest-black (>=0.3.7) ; platform_python_implementation != \"PyPy\"", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-flake8", "pytest-mypy (>=0.9.1) ; platform_python_implementation != \"PyPy\"", "pytest-virtualenv", "types-setuptools"] [[package]] name = "pytest-xdist" @@ -950,6 +997,7 @@ version = "3.6.1" description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"}, {file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"}, @@ -970,6 +1018,7 @@ version = "2.9.0.post0" description = "Extensions to the standard Python datetime module" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["dev"] files = [ {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, @@ -984,6 +1033,7 @@ version = "6.0.2" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, @@ -1046,6 +1096,7 @@ version = "0.1" description = "A custom YAML tag for referencing environment variables in YAML files. " optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, @@ -1060,6 +1111,7 @@ version = "13.9.4" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.8.0" +groups = ["dev"] files = [ {file = "rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90"}, {file = "rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098"}, @@ -1079,6 +1131,7 @@ version = "0.9.1" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "ruff-0.9.1-py3-none-linux_armv6l.whl", hash = "sha256:84330dda7abcc270e6055551aca93fdde1b0685fc4fd358f26410f9349cf1743"}, {file = "ruff-0.9.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3cae39ba5d137054b0e5b472aee3b78a7c884e61591b100aeb544bcd1fc38d4f"}, @@ -1106,6 +1159,7 @@ version = "1.5.4" description = "Tool to Detect Surrounding Shell" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, @@ -1117,6 +1171,7 @@ version = "1.17.0" description = "Python 2 and 3 compatibility utilities" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["dev"] files = [ {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, @@ -1128,6 +1183,8 @@ version = "2.2.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "python_version < \"3.11\"" files = [ {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, @@ -1169,6 +1226,7 @@ version = "0.13.2" description = "Style preserving TOML library" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde"}, {file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"}, @@ -1180,6 +1238,7 @@ version = "0.15.1" description = "Typer, build great CLIs. Easy to code. Based on Python type hints." optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "typer-0.15.1-py3-none-any.whl", hash = "sha256:7994fb7b8155b64d3402518560648446072864beefd44aa2dc36972a5972e847"}, {file = "typer-0.15.1.tar.gz", hash = "sha256:a0588c0a7fa68a1978a069818657778f86abe6ff5ea6abf472f940a08bfe4f0a"}, @@ -1197,6 +1256,7 @@ version = "6.0.12.20241230" description = "Typing stubs for PyYAML" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "types_PyYAML-6.0.12.20241230-py3-none-any.whl", hash = "sha256:fa4d32565219b68e6dee5f67534c722e53c00d1cfc09c435ef04d7353e1e96e6"}, {file = "types_pyyaml-6.0.12.20241230.tar.gz", hash = "sha256:7f07622dbd34bb9c8b264fe860a17e0efcad00d50b5f27e93984909d9363498c"}, @@ -1208,6 +1268,7 @@ version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, @@ -1219,6 +1280,7 @@ version = "6.0.0" description = "Filesystem events monitoring" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26"}, {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112"}, @@ -1261,6 +1323,7 @@ version = "10.0" description = "Wildcard/glob file name matcher." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "wcmatch-10.0-py3-none-any.whl", hash = "sha256:0dd927072d03c0a6527a20d2e6ad5ba8d0380e60870c383bc533b71744df7b7a"}, {file = "wcmatch-10.0.tar.gz", hash = "sha256:e72f0de09bba6a04e0de70937b0cf06e55f36f37b3deb422dfaf854b867b840a"}, @@ -1275,6 +1338,7 @@ version = "1.35.1" description = "A linter for YAML files." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "yamllint-1.35.1-py3-none-any.whl", hash = "sha256:2e16e504bb129ff515b37823b472750b36b6de07963bd74b307341ef5ad8bdc3"}, {file = "yamllint-1.35.1.tar.gz", hash = "sha256:7a003809f88324fd2c877734f2d575ee7881dd9043360657cc8049c809eba6cd"}, @@ -1293,20 +1357,22 @@ version = "3.21.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.9" +groups = ["dev"] +markers = "python_version < \"3.10\"" files = [ {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"}, {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] -test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +test = ["big-O", "importlib-resources ; python_version < \"3.9\"", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] type = ["pytest-mypy"] [metadata] -lock-version = "2.0" +lock-version = "2.1" python-versions = ">=3.9.0,<4.0" content-hash = "bb5271133ee0c5daa0512458b4890fd19b0c6bfee4f2a706573c74b740e001d3" From 5a5614bd642d90fd25b94c71a18bd639575927a6 Mon Sep 17 00:00:00 2001 From: Carlos Huaccho Date: Wed, 16 Apr 2025 15:43:09 -0400 Subject: [PATCH 4/7] removied undodir and added it to .gitignore --- .gitignore | 4 ++++ ...%home%huacc%hier_config%hier_config%child.py | Bin 75545 -> 0 bytes 2 files changed, 4 insertions(+) delete mode 100644 .undodir/%home%huacc%hier_config%hier_config%child.py diff --git a/.gitignore b/.gitignore index 6a9617f5..19acbe19 100644 --- a/.gitignore +++ b/.gitignore @@ -110,3 +110,7 @@ venv.bak/ # vscode .vscode/ .qodo + +# undodir +.undodir/ +*.undodir/ \ No newline at end of file diff --git a/.undodir/%home%huacc%hier_config%hier_config%child.py b/.undodir/%home%huacc%hier_config%hier_config%child.py deleted file mode 100644 index a955245aa97f7e28c44d27b62b202fab2a2812b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 75545 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 Date: Wed, 16 Apr 2025 16:09:16 -0400 Subject: [PATCH 5/7] Fixing errors from running local tests --- hier_config/child.py | 11 +++++----- hier_config/platforms/driver_base.py | 30 ---------------------------- hier_config/platforms/functions.py | 4 ++-- 3 files changed, 7 insertions(+), 38 deletions(-) diff --git a/hier_config/child.py b/hier_config/child.py index 45c9befa..160fb673 100644 --- a/hier_config/child.py +++ b/hier_config/child.py @@ -1,6 +1,5 @@ from __future__ import annotations -from collections.abc import Iterator from itertools import chain from logging import Logger, getLogger from re import search @@ -89,7 +88,7 @@ def __eq__(self, other: object) -> bool: return self.children == other.children def __ne__(self, other: object) -> bool: - return not self.__eq__(other=other) + return not self.__eq__(other) @property def driver(self) -> HConfigDriverBase: @@ -227,8 +226,8 @@ def cisco_style_text( """Yields a Cisco style formated line. Args: - style str: The style to use. Defaults to "without_comments". - tag Optional[str]: The tag to filter by. Defaults to None. + style: The style to use. Defaults to 'without_comments'. + tag: The tag to filter by. Defaults to None. Returns: str: Indentation + text + comments if any. @@ -376,7 +375,7 @@ def overwrite_with( if negated := delta.children.get(key=self.text): negated.negate() else: - negated: HConfigChild = delta.add_child( + negated = delta.add_child( text=self.text, check_if_present=False ).negate() @@ -440,7 +439,7 @@ def all_children_sorted_by_tags( def is_lineage_match(self, rules: tuple[MatchRule, ...]) -> bool: """A generic test against a lineage of HConfigChild objects.""" - lineage: tuple[HConfigBase, ...] = tuple(self.lineage()) + lineage: tuple[HConfigChild, ...] = tuple(self.lineage()) return len(rules) == len(lineage) and all( child.is_match( diff --git a/hier_config/platforms/driver_base.py b/hier_config/platforms/driver_base.py index bc99fd28..bc69a3fb 100644 --- a/hier_config/platforms/driver_base.py +++ b/hier_config/platforms/driver_base.py @@ -99,15 +99,6 @@ def idempotent_for( return None def negate_with(self, config: HConfigChild) -> Optional[str]: - """Determine if the `config` should be negated. - - Args: - config (HConfigChild): The child config object to check. - - Returns: - Optional[str]: _description_ - - """ """Determine if `config` should be negated. Args: @@ -131,15 +122,6 @@ def swap_negation(self, child: HConfigChild) -> HConfigChild: Returns: HConfigChild: The child config object with negation swapped. - """ - """Swap negation of a `child.text`. - - Args: - child (HConfigChild): The child to swap negation. - - Returns: - HConfigChild: The child with negation swapped. - """ if child.text.startswith(self.negation_prefix): child.text = child.text_without_negation @@ -170,18 +152,6 @@ def negation_prefix(self) -> str: @staticmethod def config_preprocessor(config_text: str) -> str: - """Preprocesses raw configuration text before parsing. - - By default, returns the input unchanged. Subclasses can override - this to normalize, clean, or transform config text as needed. - - Args: - config_text: The raw configuration string. - - Returns: - str: The (optionally modified) configuration string. - - """ """Preprocess the config text. Args: diff --git a/hier_config/platforms/functions.py b/hier_config/platforms/functions.py index f571b4e0..30b70c76 100644 --- a/hier_config/platforms/functions.py +++ b/hier_config/platforms/functions.py @@ -27,7 +27,7 @@ def expand_range(number_range_str: str) -> tuple[int, ...]: message: str = f"Invalid range: {number_range}" raise ValueError(message) if len(set(numbers)) != len(numbers): - message: str = "len(set(numbers)) must be equal to len(numbers)." + message = "len(set(numbers)) must be equal to len(numbers)." raise ValueError(message) return tuple(numbers) @@ -58,7 +58,7 @@ def convert_to_set_commands(config_raw: str) -> str: # Strip ; from the end of the line if stripped_line.endswith(";"): - stripped_line: str = stripped_line.replace(";", "") + stripped_line = stripped_line.replace(";", "") # Count the number of spaces at the beginning to determine the level level: int = line.find(stripped_line) // 4 From 0a7c9c32e53fc6a7b694bf76accb5f3993d741a3 Mon Sep 17 00:00:00 2001 From: Carlos Huaccho Date: Wed, 16 Apr 2025 16:12:53 -0400 Subject: [PATCH 6/7] removing inner .gitignore file --- hier_config/.gitignore | 1 - 1 file changed, 1 deletion(-) delete mode 100644 hier_config/.gitignore diff --git a/hier_config/.gitignore b/hier_config/.gitignore deleted file mode 100644 index 45784b06..00000000 --- a/hier_config/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.qodo From c3bf66a42c6c1d586a25b154315bace68a1f999f Mon Sep 17 00:00:00 2001 From: Carlos Huaccho Date: Wed, 16 Apr 2025 16:23:30 -0400 Subject: [PATCH 7/7] reverted poetry back to what HEAD has --- poetry.lock | 90 +++++++------------------------------------------- pyproject.toml | 2 +- 2 files changed, 13 insertions(+), 79 deletions(-) diff --git a/poetry.lock b/poetry.lock index 74a856a2..5ac9b89f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. [[package]] name = "annotated-types" @@ -6,7 +6,6 @@ version = "0.7.0" description = "Reusable constraint types to use with typing.Annotated" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] files = [ {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, @@ -18,7 +17,6 @@ version = "0.8.1" description = "Read/rewrite/write Python ASTs" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" -groups = ["dev"] files = [ {file = "astor-0.8.1-py2.py3-none-any.whl", hash = "sha256:070a54e890cefb5b3739d19f30f5a5ec840ffc9c50ffa7d23cc9fc1a38ebbfc5"}, {file = "astor-0.8.1.tar.gz", hash = "sha256:6a6effda93f4e1ce9f618779b2dd1d9d84f1e32812c23a29b3fff6fd7f63fa5e"}, @@ -30,7 +28,6 @@ version = "3.3.8" description = "An abstract syntax tree for Python with inference support." optional = false python-versions = ">=3.9.0" -groups = ["dev"] files = [ {file = "astroid-3.3.8-py3-none-any.whl", hash = "sha256:187ccc0c248bfbba564826c26f070494f7bc964fd286b6d9fff4420e55de828c"}, {file = "astroid-3.3.8.tar.gz", hash = "sha256:a88c7994f914a4ea8572fac479459f4955eeccc877be3f2d959a33273b0cf40b"}, @@ -45,7 +42,6 @@ version = "2.5.post1" description = "Bash style brace expander." optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "bracex-2.5.post1-py3-none-any.whl", hash = "sha256:13e5732fec27828d6af308628285ad358047cec36801598368cb28bc631dbaf6"}, {file = "bracex-2.5.post1.tar.gz", hash = "sha256:12c50952415bfa773d2d9ccb8e79651b8cdb1f31a42f6091b804f6ba2b4a66b6"}, @@ -57,7 +53,6 @@ version = "8.1.8" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" -groups = ["dev"] files = [ {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, @@ -72,8 +67,6 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["dev"] -markers = "platform_system == \"Windows\" or sys_platform == \"win32\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -85,7 +78,6 @@ version = "7.6.10" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "coverage-7.6.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5c912978f7fbf47ef99cec50c4401340436d200d41d714c7a4766f377c5b7b78"}, {file = "coverage-7.6.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a01ec4af7dfeb96ff0078ad9a48810bb0cc8abcb0115180c6013a6b26237626c"}, @@ -155,7 +147,7 @@ files = [ tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} [package.extras] -toml = ["tomli ; python_full_version <= \"3.11.0a6\""] +toml = ["tomli"] [[package]] name = "dill" @@ -163,7 +155,6 @@ version = "0.3.9" description = "serialize all of Python" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "dill-0.3.9-py3-none-any.whl", hash = "sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a"}, {file = "dill-0.3.9.tar.gz", hash = "sha256:81aa267dddf68cbfe8029c42ca9ec6a4ab3b22371d1c450abc54422577b4512c"}, @@ -179,8 +170,6 @@ version = "1.2.2" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" -groups = ["dev"] -markers = "python_version < \"3.11\"" files = [ {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, @@ -195,7 +184,6 @@ version = "2.1.1" description = "execnet: rapid multi-Python deployment" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc"}, {file = "execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"}, @@ -210,7 +198,6 @@ version = "1.0.1" description = "CLI tool to convert a python project's %-formatted strings to f-strings." optional = false python-versions = ">=3.7" -groups = ["dev"] files = [ {file = "flynt-1.0.1-py3-none-any.whl", hash = "sha256:65d1c546434827275123222a98408e9561bcd67db832dd58f530ff17b8329ec1"}, {file = "flynt-1.0.1.tar.gz", hash = "sha256:988aac00672a5469726cc0a17cef7d1178c284a9fe8563458db2475d0aaed965"}, @@ -229,7 +216,6 @@ version = "2.1.0" description = "Copy your docs directly to the gh-pages branch." optional = false python-versions = "*" -groups = ["dev"] files = [ {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, @@ -247,7 +233,6 @@ version = "2024.6.6" description = "Generate a dot graph from the output of several profilers." optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "gprof2dot-2024.6.6-py2.py3-none-any.whl", hash = "sha256:45b14ad7ce64e299c8f526881007b9eb2c6b75505d5613e96e66ee4d5ab33696"}, {file = "gprof2dot-2024.6.6.tar.gz", hash = "sha256:fa1420c60025a9eb7734f65225b4da02a10fc6dd741b37fa129bc6b41951e5ab"}, @@ -259,8 +244,6 @@ version = "8.5.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" -groups = ["dev"] -markers = "python_version < \"3.10\"" files = [ {file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"}, {file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"}, @@ -270,12 +253,12 @@ files = [ zipp = ">=3.20" [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] perf = ["ipython"] -test = ["flufl.flake8", "importlib-resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] type = ["pytest-mypy"] [[package]] @@ -284,7 +267,6 @@ version = "2.0.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.7" -groups = ["dev"] files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, @@ -296,7 +278,6 @@ version = "5.13.2" description = "A Python utility / library to sort Python imports." optional = false python-versions = ">=3.8.0" -groups = ["dev"] files = [ {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, @@ -311,7 +292,6 @@ version = "3.1.5" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" -groups = ["dev"] files = [ {file = "jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb"}, {file = "jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb"}, @@ -329,7 +309,6 @@ version = "3.7" description = "Python implementation of John Gruber's Markdown." optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803"}, {file = "markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2"}, @@ -348,7 +327,6 @@ version = "3.0.0" description = "Python port of markdown-it. Markdown parsing, done right!" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, @@ -373,7 +351,6 @@ version = "3.0.2" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, @@ -444,7 +421,6 @@ version = "0.7.0" description = "McCabe checker, plugin for flake8" optional = false python-versions = ">=3.6" -groups = ["dev"] files = [ {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, @@ -456,7 +432,6 @@ version = "0.1.2" description = "Markdown URL utilities" optional = false python-versions = ">=3.7" -groups = ["dev"] files = [ {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, @@ -468,7 +443,6 @@ version = "1.3.4" description = "A deep merge function for 🐍." optional = false python-versions = ">=3.6" -groups = ["dev"] files = [ {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, @@ -480,7 +454,6 @@ version = "1.6.1" description = "Project documentation with Markdown." optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e"}, {file = "mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2"}, @@ -504,7 +477,7 @@ watchdog = ">=2.0" [package.extras] i18n = ["babel (>=2.9.0)"] -min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4) ; platform_system == \"Windows\"", "ghp-import (==1.0)", "importlib-metadata (==4.4) ; python_version < \"3.10\"", "jinja2 (==2.11.1)", "markdown (==3.3.6)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "mkdocs-get-deps (==0.2.0)", "packaging (==20.5)", "pathspec (==0.11.1)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "watchdog (==2.0)"] +min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-import (==1.0)", "importlib-metadata (==4.4)", "jinja2 (==2.11.1)", "markdown (==3.3.6)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "mkdocs-get-deps (==0.2.0)", "packaging (==20.5)", "pathspec (==0.11.1)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "watchdog (==2.0)"] [[package]] name = "mkdocs-get-deps" @@ -512,7 +485,6 @@ version = "0.2.0" description = "MkDocs extension that lists all dependencies according to a mkdocs.yml file" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134"}, {file = "mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c"}, @@ -530,7 +502,6 @@ version = "7.1.2" description = "Mkdocs Markdown includer plugin." optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "mkdocs_include_markdown_plugin-7.1.2-py3-none-any.whl", hash = "sha256:ff1175d1b4f83dea6a38e200d6f0c3db10308975bf60c197d31172671753dbc4"}, {file = "mkdocs_include_markdown_plugin-7.1.2.tar.gz", hash = "sha256:1b393157b1aa231b0e6c59ba80f52b723f4b7827bb7a1264b505334f8542aaf1"}, @@ -549,7 +520,6 @@ version = "1.14.1" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "mypy-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:52686e37cf13d559f668aa398dd7ddf1f92c5d613e4f8cb262be2fb4fedb0fcb"}, {file = "mypy-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1fb545ca340537d4b45d3eecdb3def05e913299ca72c290326be19b3804b39c0"}, @@ -609,7 +579,6 @@ version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.5" -groups = ["dev"] 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"}, @@ -621,7 +590,6 @@ version = "1.9.1" description = "Node.js virtual environment builder" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["dev"] files = [ {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, @@ -633,7 +601,6 @@ version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, @@ -645,7 +612,6 @@ version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, @@ -657,7 +623,6 @@ version = "4.3.6" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, @@ -674,7 +639,6 @@ version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, @@ -690,7 +654,6 @@ version = "2.10.5" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] files = [ {file = "pydantic-2.10.5-py3-none-any.whl", hash = "sha256:4dd4e322dbe55472cb7ca7e73f4b63574eecccf2835ffa2af9021ce113c83c53"}, {file = "pydantic-2.10.5.tar.gz", hash = "sha256:278b38dbbaec562011d659ee05f63346951b3a248a6f3642e1bc68894ea2b4ff"}, @@ -703,7 +666,7 @@ typing-extensions = ">=4.12.2" [package.extras] email = ["email-validator (>=2.0.0)"] -timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""] +timezone = ["tzdata"] [[package]] name = "pydantic-core" @@ -711,7 +674,6 @@ version = "2.27.2" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] files = [ {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"}, {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"}, @@ -824,7 +786,6 @@ version = "2.19.1" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, @@ -839,7 +800,6 @@ version = "3.3.3" description = "python code static checker" optional = false python-versions = ">=3.9.0" -groups = ["dev"] files = [ {file = "pylint-3.3.3-py3-none-any.whl", hash = "sha256:26e271a2bc8bce0fc23833805a9076dd9b4d5194e2a02164942cb3cdc37b4183"}, {file = "pylint-3.3.3.tar.gz", hash = "sha256:07c607523b17e6d16e2ae0d7ef59602e332caa762af64203c24b41c27139f36a"}, @@ -851,7 +811,7 @@ colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} dill = [ {version = ">=0.2", markers = "python_version < \"3.11\""}, {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, - {version = ">=0.3.6", markers = "python_version == \"3.11\""}, + {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, ] isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" mccabe = ">=0.6,<0.8" @@ -870,7 +830,6 @@ version = "0.8.2" description = "Utilities and helpers for writing Pylint plugins" optional = false python-versions = ">=3.7,<4.0" -groups = ["dev"] files = [ {file = "pylint_plugin_utils-0.8.2-py3-none-any.whl", hash = "sha256:ae11664737aa2effbf26f973a9e0b6779ab7106ec0adc5fe104b0907ca04e507"}, {file = "pylint_plugin_utils-0.8.2.tar.gz", hash = "sha256:d3cebf68a38ba3fba23a873809155562571386d4c1b03e5b4c4cc26c3eee93e4"}, @@ -885,7 +844,6 @@ version = "0.3.5" description = "A Pylint plugin to help Pylint understand the Pydantic" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "pylint_pydantic-0.3.5-py3-none-any.whl", hash = "sha256:e7a54f09843b000676633ed02d5985a4a61c8da2560a3b0d46082d2ff171c4a1"}, ] @@ -901,7 +859,6 @@ version = "1.1.391" description = "Command line wrapper for pyright" optional = false python-versions = ">=3.7" -groups = ["dev"] files = [ {file = "pyright-1.1.391-py3-none-any.whl", hash = "sha256:54fa186f8b3e8a55a44ebfa842636635688670c6896dcf6cf4a7fc75062f4d15"}, {file = "pyright-1.1.391.tar.gz", hash = "sha256:66b2d42cdf5c3cbab05f2f4b76e8bec8aa78e679bfa0b6ad7b923d9e027cadb2"}, @@ -922,7 +879,6 @@ version = "8.3.4" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, @@ -945,7 +901,6 @@ version = "6.0.0" description = "Pytest plugin for measuring coverage." optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0"}, {file = "pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35"}, @@ -964,7 +919,6 @@ version = "1.8.1" description = "Profiling plugin for py.test" optional = false python-versions = ">=3.6" -groups = ["dev"] files = [ {file = "pytest-profiling-1.8.1.tar.gz", hash = "sha256:3f171fa69d5c82fa9aab76d66abd5f59da69135c37d6ae5bf7557f1b154cb08d"}, {file = "pytest_profiling-1.8.1-py3-none-any.whl", hash = "sha256:3dd8713a96298b42d83de8f5951df3ada3e61b3e5d2a06956684175529e17aea"}, @@ -981,7 +935,6 @@ version = "6.0.1" description = "Invoke py.test as distutils command with dependency resolution" optional = false python-versions = ">=3.7" -groups = ["dev"] files = [ {file = "pytest-runner-6.0.1.tar.gz", hash = "sha256:70d4739585a7008f37bf4933c013fdb327b8878a5a69fcbb3316c88882f0f49b"}, {file = "pytest_runner-6.0.1-py3-none-any.whl", hash = "sha256:ea326ed6f6613992746062362efab70212089a4209c08d67177b3df1c52cd9f2"}, @@ -989,7 +942,7 @@ files = [ [package.extras] docs = ["jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx"] -testing = ["pytest (>=6)", "pytest-black (>=0.3.7) ; platform_python_implementation != \"PyPy\"", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-flake8", "pytest-mypy (>=0.9.1) ; platform_python_implementation != \"PyPy\"", "pytest-virtualenv", "types-setuptools"] +testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-virtualenv", "types-setuptools"] [[package]] name = "pytest-xdist" @@ -997,7 +950,6 @@ version = "3.6.1" description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"}, {file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"}, @@ -1018,7 +970,6 @@ version = "2.9.0.post0" description = "Extensions to the standard Python datetime module" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -groups = ["dev"] files = [ {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, @@ -1033,7 +984,6 @@ version = "6.0.2" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, @@ -1096,7 +1046,6 @@ version = "0.1" description = "A custom YAML tag for referencing environment variables in YAML files. " optional = false python-versions = ">=3.6" -groups = ["dev"] files = [ {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, @@ -1111,7 +1060,6 @@ version = "13.9.4" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.8.0" -groups = ["dev"] files = [ {file = "rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90"}, {file = "rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098"}, @@ -1131,7 +1079,6 @@ version = "0.9.1" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" -groups = ["dev"] files = [ {file = "ruff-0.9.1-py3-none-linux_armv6l.whl", hash = "sha256:84330dda7abcc270e6055551aca93fdde1b0685fc4fd358f26410f9349cf1743"}, {file = "ruff-0.9.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3cae39ba5d137054b0e5b472aee3b78a7c884e61591b100aeb544bcd1fc38d4f"}, @@ -1159,7 +1106,6 @@ version = "1.5.4" description = "Tool to Detect Surrounding Shell" optional = false python-versions = ">=3.7" -groups = ["dev"] files = [ {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, @@ -1171,7 +1117,6 @@ version = "1.17.0" description = "Python 2 and 3 compatibility utilities" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -groups = ["dev"] files = [ {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, @@ -1183,8 +1128,6 @@ version = "2.2.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" -groups = ["dev"] -markers = "python_version < \"3.11\"" files = [ {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, @@ -1226,7 +1169,6 @@ version = "0.13.2" description = "Style preserving TOML library" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde"}, {file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"}, @@ -1238,7 +1180,6 @@ version = "0.15.1" description = "Typer, build great CLIs. Easy to code. Based on Python type hints." optional = false python-versions = ">=3.7" -groups = ["dev"] files = [ {file = "typer-0.15.1-py3-none-any.whl", hash = "sha256:7994fb7b8155b64d3402518560648446072864beefd44aa2dc36972a5972e847"}, {file = "typer-0.15.1.tar.gz", hash = "sha256:a0588c0a7fa68a1978a069818657778f86abe6ff5ea6abf472f940a08bfe4f0a"}, @@ -1256,7 +1197,6 @@ version = "6.0.12.20241230" description = "Typing stubs for PyYAML" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "types_PyYAML-6.0.12.20241230-py3-none-any.whl", hash = "sha256:fa4d32565219b68e6dee5f67534c722e53c00d1cfc09c435ef04d7353e1e96e6"}, {file = "types_pyyaml-6.0.12.20241230.tar.gz", hash = "sha256:7f07622dbd34bb9c8b264fe860a17e0efcad00d50b5f27e93984909d9363498c"}, @@ -1268,7 +1208,6 @@ version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, @@ -1280,7 +1219,6 @@ version = "6.0.0" description = "Filesystem events monitoring" optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26"}, {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112"}, @@ -1323,7 +1261,6 @@ version = "10.0" description = "Wildcard/glob file name matcher." optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "wcmatch-10.0-py3-none-any.whl", hash = "sha256:0dd927072d03c0a6527a20d2e6ad5ba8d0380e60870c383bc533b71744df7b7a"}, {file = "wcmatch-10.0.tar.gz", hash = "sha256:e72f0de09bba6a04e0de70937b0cf06e55f36f37b3deb422dfaf854b867b840a"}, @@ -1338,7 +1275,6 @@ version = "1.35.1" description = "A linter for YAML files." optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "yamllint-1.35.1-py3-none-any.whl", hash = "sha256:2e16e504bb129ff515b37823b472750b36b6de07963bd74b307341ef5ad8bdc3"}, {file = "yamllint-1.35.1.tar.gz", hash = "sha256:7a003809f88324fd2c877734f2d575ee7881dd9043360657cc8049c809eba6cd"}, @@ -1357,22 +1293,20 @@ version = "3.21.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.9" -groups = ["dev"] -markers = "python_version < \"3.10\"" files = [ {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"}, {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] -test = ["big-O", "importlib-resources ; python_version < \"3.9\"", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] type = ["pytest-mypy"] [metadata] -lock-version = "2.1" +lock-version = "2.0" python-versions = ">=3.9.0,<4.0" -content-hash = "bb5271133ee0c5daa0512458b4890fd19b0c6bfee4f2a706573c74b740e001d3" +content-hash = "bb5271133ee0c5daa0512458b4890fd19b0c6bfee4f2a706573c74b740e001d3" \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index b37f7d44..b972c5f7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -153,4 +153,4 @@ ignore = [ parametrize-values-type = "tuple" [tool.ruff.lint.per-file-ignores] -"**/tests/*" = ["PLC2701", "S101"] +"**/tests/*" = ["PLC2701", "S101"] \ No newline at end of file