diff --git a/.cookiecutter.json b/.cookiecutter.json
new file mode 100644
index 00000000..20093dd4
--- /dev/null
+++ b/.cookiecutter.json
@@ -0,0 +1,29 @@
+{
+ "cookiecutter": {
+ "codeowner_github_usernames": "@itdependsnetworks @jeffkala @qduk",
+ "full_name": "Network to Code, LLC",
+ "email": "info@networktocode.com",
+ "github_org": "networktocode",
+ "description": "\"Common helper functions useful in network automation.\"",
+ "project_name": "netutils",
+ "project_slug": "netutils",
+ "repo_url": "https://github.com/networktocode/netutils",
+ "base_url": "netutils",
+ "project_python_name": "netutils",
+ "project_python_base_version": "3.8",
+ "project_with_config_settings": "no",
+ "generate_docs": "yes",
+ "version": "1.15.1",
+ "_drift_manager": {
+ "template": "https://github.com/networktocode-llc/cookiecutter-ntc.git",
+ "template_dir": "python",
+ "template_ref": "jlk-more-python",
+ "cookie_dir": "",
+ "branch_prefix": "drift-manager",
+ "pull_request_strategy": "create",
+ "post_actions": [],
+ "draft": false,
+ "baked_commit_ref": "36ad55b634a46615d22d393aef2d46fa383d1f93"
+ }
+ }
+}
diff --git a/.dockerignore b/.dockerignore
index e4b30e20..932ca8e2 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1,3 +1,4 @@
+<<<<<<< HEAD
**/*.pyc
**/*.pyo
**/*.log
@@ -7,4 +8,33 @@ Dockerfile
docker-compose.yml
.env
docs/_build
-**/__pycache__
\ No newline at end of file
+**/__pycache__
+=======
+# Docker related
+development/Dockerfile
+development/docker-compose*.yml
+development/*.env
+*.env
+environments/
+
+# Python
+**/*.pyc
+**/*.pyo
+**/__pycache__/
+**/.pytest_cache/
+**/.venv/
+
+
+# Other
+docs/_build
+FAQ.md
+.git/
+.gitignore
+.github
+tasks.py
+LICENSE
+**/*.log
+**/.vscode/
+invoke*.yml
+tasks.py
+>>>>>>> 5806227 (Cookie initialy baked by NetworkToCode Cookie Drift Manager Tool)
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 491431e0..7b40291a 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -4,8 +4,13 @@ about: Report a reproducible bug in the current release of netutils
---
### Environment
+<<<<<<< HEAD
* Python version:
* netutils version:
+=======
+* Python version:
+* netutils version:
+>>>>>>> 5806227 (Cookie initialy baked by NetworkToCode Cookie Drift Manager Tool)
### Expected Behavior
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
index 67ee840b..52f6c032 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -5,7 +5,11 @@ about: Propose a new feature or enhancement
---
### Environment
+<<<<<<< HEAD
* netutils version:
+=======
+* netutils version:
+>>>>>>> 5806227 (Cookie initialy baked by NetworkToCode Cookie Drift Manager Tool)
+
diff --git a/docs/user/faq.md b/docs/user/faq.md
index 60335a5b..867715b8 100644
--- a/docs/user/faq.md
+++ b/docs/user/faq.md
@@ -1,4 +1,5 @@
# Frequently Asked Questions
+<<<<<<< HEAD
## Which OS Config types have Parsers?
@@ -14,3 +15,5 @@ The documentation is always provided in the function for [code docs](../dev/code
One of the requirements of this library is to avoid having dependencies; however, including a few optional dependencies in an opt in fashion allows `netutils` to remain lean while offering some powerful addons.
Installing the optional dependencies is only needed when the user needs access to the functions using the dependencies. If the dependency is not installed the function simply raises an exception and warns the user that the library is not installed.
+=======
+>>>>>>> 5806227 (Cookie initialy baked by NetworkToCode Cookie Drift Manager Tool)
diff --git a/docs/user/lib_getting_started.md b/docs/user/lib_getting_started.md
index d6dba101..c6711545 100644
--- a/docs/user/lib_getting_started.md
+++ b/docs/user/lib_getting_started.md
@@ -1,7 +1,27 @@
# Getting Started with the Library
+<<<<<<< HEAD
The library's primary use case is providing Python functions for the community. The primary way to use it is the same as any Python library. You `import` the functions, and use them, there are many examples on the [Use Cases](./lib_use_cases.md) page.
## Install the Library
-To install the library, please follow the instructions detailed in the [Installation Guide](../admin/install.md).
\ No newline at end of file
+To install the library, please follow the instructions detailed in the [Installation Guide](../admin/install.md).
+=======
+This document provides a step-by-step tutorial on how to get the library going and how to use it.
+
+## Install the Library
+
+To install the library, please follow the instructions detailed in the [Installation Guide](../admin/install.md).
+
+## First steps with the Library
+
+!!! warning "Developer Note - Remove Me!"
+ What (with screenshots preferably) does it look like to perform the simplest workflow within the library once installed?
+
+## What are the next steps?
+
+!!! warning "Developer Note - Remove Me!"
+ After taking the first steps, what else could the users look at doing.
+
+You can check out the [Use Cases](./lib_use_cases.md) section for more examples.
+>>>>>>> 5806227 (Cookie initialy baked by NetworkToCode Cookie Drift Manager Tool)
diff --git a/docs/user/lib_overview.md b/docs/user/lib_overview.md
index b72193a0..b29caf86 100644
--- a/docs/user/lib_overview.md
+++ b/docs/user/lib_overview.md
@@ -1,5 +1,6 @@
# Library Overview
+<<<<<<< HEAD
A Python library that is a collection of functions for common network automation tasks.
This library intends to keep the following tenets:
@@ -15,10 +16,16 @@ This library intends to keep the following tenets:
## Description/Overview
A Python library that is a collection of functions that are used in the common network automation tasks. Tasks such as converting a BGP ASN to and from dotted format, normalizing an interface name, or "type 5" encrypting a password. The intention is to centralize these functions while keeping the library light.
+=======
+This document provides an overview of the library including critical information and important considerations.
+
+## Description
+>>>>>>> 5806227 (Cookie initialy baked by NetworkToCode Cookie Drift Manager Tool)
## Audience (User Personas) - Who should use this Library?
+<<<<<<< HEAD
The intended audience is those who are programming network automation tasks with Python. Whether you are a seasoned veteran or a casual scripter, this library should help to reduce duplication between various reinventing the wheel.
## Authors and Maintainers
@@ -26,3 +33,12 @@ The intended audience is those who are programming network automation tasks with
- @itdependsnetworks
- @jeffkala
- @qduk
+=======
+!!! warning "Developer Note - Remove Me!"
+ Who is this meant for/ who is the common user of this library?
+
+## Authors and Maintainers
+
+!!! warning "Developer Note - Remove Me!"
+ Add the team and/or the main individuals maintaining this project. Include historical maintainers as well.
+>>>>>>> 5806227 (Cookie initialy baked by NetworkToCode Cookie Drift Manager Tool)
diff --git a/docs/user/lib_use_cases.md b/docs/user/lib_use_cases.md
index ada1e9fc..17c0aab7 100644
--- a/docs/user/lib_use_cases.md
+++ b/docs/user/lib_use_cases.md
@@ -1,5 +1,6 @@
# Using the Library
+<<<<<<< HEAD
As the Python library is intended to be a low-level implementation, the primary use case is within Code. On this page as well as subsequent pages we will provide an overview on the type of functions and examples.
# Function Groupings
@@ -84,3 +85,15 @@ Often times interfaces will come in various different shortened names, and it is
!!! tip
These are just some examples of the many functions provided by this library. Please review the Developer code for Examples on every public function
+=======
+This document describes common use-cases and scenarios for this library.
+
+## General Usage
+
+## Use-cases and common workflows
+
+## Screenshots
+
+!!! warning "Developer Note - Remove Me!"
+ Ideally captures every view exposed by the Library. Should include a relevant dataset.
+>>>>>>> 5806227 (Cookie initialy baked by NetworkToCode Cookie Drift Manager Tool)
diff --git a/example.invoke.yml b/example.invoke.yml
new file mode 100644
index 00000000..8a6890e7
--- /dev/null
+++ b/example.invoke.yml
@@ -0,0 +1,7 @@
+---
+"netutils":
+ python_ver: "3.8"
+ local: false
+ # image_name: "netutils"
+ # image_ver: "latest"
+ # pwd: "."
diff --git a/mkdocs.yml b/mkdocs.yml
index 5fb748b5..85fe7837 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -1,10 +1,18 @@
---
dev_addr: "127.0.0.1:8001"
+<<<<<<< HEAD
edit_uri: "edit/develop/docs"
site_dir: "netutils/static/netutils/docs"
site_name: "Netutils Documentation"
site_url: "https://netutils.readthedocs.io/en/latest/"
repo_url: "https://github.com/networktocode/netutils"
+=======
+edit_uri: "edit/main/netutils/docs"
+site_dir: "netutils/static/netutils/docs"
+site_name: "netutils Documentation"
+site_url: "https://netutils.readthedocs.io/"
+repo_url: "https://github.com/networktocode-llc/netutils"
+>>>>>>> 5806227 (Cookie initialy baked by NetworkToCode Cookie Drift Manager Tool)
copyright: "Copyright © The Authors"
theme:
name: "material"
@@ -14,7 +22,14 @@ theme:
- "python"
- "yaml"
features:
+<<<<<<< HEAD
+ - "content.code.copy"
+=======
+ - "content.action.edit"
+ - "content.action.view"
- "content.code.copy"
+ - "navigation.footer"
+>>>>>>> 5806227 (Cookie initialy baked by NetworkToCode Cookie Drift Manager Tool)
- "navigation.indexes"
- "navigation.tabs"
- "navigation.tabs.sticky"
@@ -43,12 +58,15 @@ theme:
extra_css:
- "assets/extra.css"
+<<<<<<< HEAD
# needed for RTD version flyout menu
# jquery is not (yet) injected by RTD automatically and it might be dropped
# as a dependency in the future
extra_javascript:
- "https://code.jquery.com/jquery-3.6.0.min.js"
+=======
+>>>>>>> 5806227 (Cookie initialy baked by NetworkToCode Cookie Drift Manager Tool)
extra:
generator: false
ntc_sponsor: true
@@ -76,10 +94,21 @@ markdown_extensions:
permalink: true
- "attr_list"
- "md_in_html"
+<<<<<<< HEAD
+=======
+ - "markdown_data_tables":
+ base_path: "docs"
+ - "pymdownx.details"
+ # Need pymdownx.emoji for Grid icon search
+ - pymdownx.emoji:
+ emoji_index: !!python/name:material.extensions.emoji.twemoji
+ emoji_generator: !!python/name:material.extensions.emoji.to_svg
+>>>>>>> 5806227 (Cookie initialy baked by NetworkToCode Cookie Drift Manager Tool)
- "pymdownx.highlight":
anchor_linenums: true
- "pymdownx.inlinehilite"
- "pymdownx.snippets"
+<<<<<<< HEAD
- "pymdownx.superfences"
- "footnotes"
plugins:
@@ -90,20 +119,53 @@ plugins:
- "netutils.config.parser"
classy_libraries:
- "netutils"
+=======
+ - "pymdownx.superfences":
+ custom_fences:
+ - name: "mermaid"
+ class: "mermaid"
+ format: !!python/name:pymdownx.superfences.fence_code_format
+ - "pymdownx.tabbed":
+ "alternate_style": true
+ - "pymdownx.tilde"
+
+plugins:
+ - "search"
+ - "gen-files":
+ scripts:
+ - "docs/generate_code_reference_pages.py"
+ - "glightbox":
+ manual: true # See https://blueswen.github.io/mkdocs-glightbox/flexibility/enable-by-image-or-page/
+ - "section-index"
+>>>>>>> 5806227 (Cookie initialy baked by NetworkToCode Cookie Drift Manager Tool)
- "mkdocstrings":
default_handler: "python"
handlers:
python:
paths: ["."]
options:
+<<<<<<< HEAD
show_root_heading: true
watch:
- "README.md"
+=======
+ heading_level: 1
+ show_root_heading: true
+ show_root_members_full_path: true
+ show_source: false
+
+validation:
+ absolute_links: "warn"
+ anchors: "warn"
+ omitted_files: "warn"
+ unrecognized_links: "warn"
+>>>>>>> 5806227 (Cookie initialy baked by NetworkToCode Cookie Drift Manager Tool)
nav:
- Overview: "index.md"
- User Guide:
- Library Overview: "user/lib_overview.md"
+<<<<<<< HEAD
- Using the Library: "user/lib_use_cases.md"
- Jinja Filters: "user/lib_use_cases_jinja_filters.md"
- Library Mapper: "user/lib_use_cases_lib_mapper.md"
@@ -114,6 +176,10 @@ nav:
- ACL: "user/lib_use_cases_acl.md"
- Upgrade Paths: "user/lib_upgrade_paths.md"
- Getting Started: "user/lib_getting_started.md"
+=======
+ - Getting Started: "user/lib_getting_started.md"
+ - Using the Library: "user/lib_use_cases.md"
+>>>>>>> 5806227 (Cookie initialy baked by NetworkToCode Cookie Drift Manager Tool)
- Frequently Asked Questions: "user/faq.md"
- Administrator Guide:
- Install and Configure: "admin/install.md"
@@ -121,6 +187,7 @@ nav:
- Uninstall: "admin/uninstall.md"
- Release Notes:
- "admin/release_notes/index.md"
+<<<<<<< HEAD
- v1.15: "admin/release_notes/version_1.15.md"
- v1.14: "admin/release_notes/version_1.14.md"
- v1.13: "admin/release_notes/version_1.13.md"
@@ -139,11 +206,15 @@ nav:
- v1.0: "admin/release_notes/version_1.0.md"
- v0.2: "admin/release_notes/version_0.2.md"
- v0.1: "admin/release_notes/version_0.1.md"
+=======
+ - v1.0: "admin/release_notes/version_1.0.md"
+>>>>>>> 5806227 (Cookie initialy baked by NetworkToCode Cookie Drift Manager Tool)
- Developer Guide:
- Extending the Library: "dev/extending.md"
- Contributing to the Library: "dev/contributing.md"
- Development Environment: "dev/dev_environment.md"
- Release Checklist: "dev/release_checklist.md"
+<<<<<<< HEAD
- Config Parser Development: "dev/dev_config.md"
- Arch Decision Records: "dev/dev_adr.md"
- Code Attribution to the Library: "dev/attribution.md"
@@ -177,3 +248,6 @@ nav:
not_in_nav: |
- "user/include_jinja_list.md"
- "user/lib_mapper/*.md"
+=======
+ - Architecture Design Records: "dev/arch_decision.md"
+>>>>>>> 5806227 (Cookie initialy baked by NetworkToCode Cookie Drift Manager Tool)
diff --git a/netutils/api.py b/netutils/api.py
new file mode 100644
index 00000000..a92b3f3b
--- /dev/null
+++ b/netutils/api.py
@@ -0,0 +1,3 @@
+"""Example API."""
+
+# Fill in with information regarding Python API for project
diff --git a/netutils/cli.py b/netutils/cli.py
new file mode 100644
index 00000000..92abd247
--- /dev/null
+++ b/netutils/cli.py
@@ -0,0 +1,27 @@
+"""Example cli using click."""
+
+import logging
+
+import click
+
+from netutils.log import initialize_logging
+
+# Import necessary project related things to use in CLI
+
+log = logging.getLogger(__name__)
+
+
+@click.command()
+@click.option("--test", default="Test Output", help="Test argument")
+@click.option(
+ "--log-level",
+ default="INFO",
+ type=click.Choice(["NOTSET", "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]),
+ help="Logging level",
+)
+@click.option("--log-file", default=None, help="Log file to output to debug logs to.")
+def main(test, log_level, log_file):
+ """Entrypoint into CLI app."""
+ initialize_logging(level=log_level, filename=log_file)
+ log.info("Entrypoint of the CLI app.")
+ print(test)
diff --git a/netutils/log.py b/netutils/log.py
new file mode 100644
index 00000000..41002801
--- /dev/null
+++ b/netutils/log.py
@@ -0,0 +1,77 @@
+"""
+Logging utilities for netutils.
+
+This module contains helpers and wrappers for making logging more consistent across applications.
+
+How to use me:
+
+ >>> from netutils.log import initialize_logging
+ >>> log = initialize_logging(level="debug")
+ 2021-12-07T10:51:49-0700 [DEBUG] [log] [initialize_logging] cookiecutter_project: Logging initialized.
+ >>> log.info("NTC")
+ 2021-12-07T10:51:49-0700 [INFO] [cli] [main] cookiecutter_project.cli: Entrypoint of the CLI app.
+"""
+
+import logging.config
+
+APP = "netutils"
+
+
+def initialize_logging(config=None, level="INFO", filename=None):
+ """Initialize logging using sensible defaults.
+
+ Args:
+ config (dict): User provided configuration dictionary.
+ level (str): The level of logging for STDOUT logging.
+ filename (str): Where to output debug logging to file.
+
+ """
+ if not config:
+ config = {
+ "version": 1,
+ "disable_existing_loggers": False,
+ "formatters": {
+ "standard": {
+ "format": "%(asctime)s [%(levelname)s] %(name)s: %(message)s",
+ "datefmt": "%Y-%m-%dT%H:%M:%S%z",
+ },
+ "debug": {
+ "format": "%(asctime)s [%(levelname)s] [%(module)s] [%(funcName)s] %(name)s: %(message)s",
+ "datefmt": "%Y-%m-%dT%H:%M:%S%z",
+ },
+ },
+ "handlers": {
+ "standard": {
+ "class": "logging.StreamHandler",
+ "formatter": "standard",
+ "level": level.upper(),
+ },
+ },
+ "loggers": {
+ "": {
+ "handlers": ["standard"],
+ "level": "DEBUG",
+ }
+ },
+ }
+
+ # If a filename is passed in, let's add a FileHandler
+ if filename:
+ config["handlers"].update(
+ {
+ "file_output": {
+ "class": "logging.FileHandler",
+ "formatter": "debug",
+ "level": "DEBUG",
+ "filename": filename,
+ }
+ }
+ )
+ config["loggers"][""]["handlers"].append("file_output")
+
+ # Configure the logging
+ logging.config.dictConfig(config)
+
+ # Initialize root logger and advise logging has been initialized
+ log = logging.getLogger(APP)
+ log.debug("Logging initialized.")
diff --git a/pyproject.toml b/pyproject.toml
index 9d2bdc64..eb7e7c82 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,5 +1,6 @@
[tool.poetry]
name = "netutils"
+<<<<<<< HEAD
version = "1.15.2a0"
description = "Common helper functions useful in network automation."
authors = ["Network to Code, LLC "]
@@ -9,6 +10,13 @@ repository = "https://github.com/networktocode/netutils"
documentation = "https://netutils.readthedocs.io"
readme = "README.md"
keywords = ["netutils", "network utils", "network utilities", "net-utils"]
+=======
+version = "1.15.1"
+description = ""Common helper functions useful in network automation.""
+authors = ["Network to Code, LLC "]
+readme = "README.md"
+license = "Apache-2.0"
+>>>>>>> 5806227 (Cookie initialy baked by NetworkToCode Cookie Drift Manager Tool)
classifiers = [
"Intended Audience :: Developers",
"Development Status :: 5 - Production/Stable",
@@ -17,16 +25,25 @@ classifiers = [
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
+<<<<<<< HEAD
"Programming Language :: Python :: 3.13",
]
include = [
"LICENSE",
"README.md",
"netutils/protocols.json"
+=======
+]
+
+include = [
+ "LICENSE",
+ "README.md",
+>>>>>>> 5806227 (Cookie initialy baked by NetworkToCode Cookie Drift Manager Tool)
]
[tool.poetry.dependencies]
python = "^3.8"
+<<<<<<< HEAD
napalm = {version = "^4.0.0", optional = true}
jsonschema = {version = "^4.17.3", optional = true}
legacycrypt = {version = "0.3", optional = true}
@@ -59,6 +76,60 @@ mkdocstrings-python = "1.10.8"
mkdocs-autorefs = "1.2.0"
griffe = "1.1.1"
mkdocs-python-classy = "0.1.3"
+=======
+click = "*"
+
+[tool.poetry.group.dev.dependencies]
+requests_mock = "*"
+pytest = "*"
+mock = "*"
+pyyaml = "^6.0.1"
+pylint = "^3.1.0"
+yamllint = "^1.35.1"
+invoke = "^2.2.0"
+toml = "^0.10.2"
+attrs = "^23.2.0"
+towncrier = ">=23.6.0,<=24.8.0"
+ruff = "*"
+Markdown = "*"
+
+[tool.poetry.group.docs.dependencies]
+# Rendering docs to HTML
+mkdocs = "1.6.1"
+# Embedding YAML files into Markdown documents as tables
+markdown-data-tables = "1.0.0"
+# Render custom markdown for version added/changed/remove notes
+markdown-version-annotations = "1.0.1"
+# Automatically generate some files as part of mkdocs build
+mkdocs-gen-files = "0.5.0"
+# Image lightboxing in mkdocs
+mkdocs-glightbox = "0.4.0"
+# Use Jinja2 templating in docs - see settings.md
+mkdocs-macros-plugin = "1.3.7"
+# Material for mkdocs theme
+mkdocs-material = "9.6.15"
+# Handle docs redirections
+mkdocs-redirects = "1.2.2"
+# Automatically handle index pages for docs sections
+mkdocs-section-index = "0.3.10"
+# Automatic documentation from sources, for MkDocs
+mkdocstrings = "0.27.0"
+# Python-specific extension to mkdocstrings
+mkdocstrings-python = "1.13.0"
+griffe = "1.1.1"
+
+[tool.poetry.scripts]
+netutils = 'netutils.cli:main'
+
+[tool.netutils]
+string_required = "some string"
+array_default = ["a", "b", "c"]
+url_default = "http://localhost"
+ip_address = "192.168.0.1"
+ip_network = "2001:db8:3c4d:15::/64"
+file = "README.md"
+
+>>>>>>> 5806227 (Cookie initialy baked by NetworkToCode Cookie Drift Manager Tool)
[tool.ruff]
line-length = 120
@@ -99,6 +170,7 @@ convention = "google"
"S"
]
+<<<<<<< HEAD
[tool.pylint.basic]
# No docstrings required for private methods (Pylint default), or for test_ functions, or for inner Meta classes.
no-docstring-rgx="^(_|test_|Meta$)"
@@ -111,6 +183,23 @@ disable = """,
line-too-long,
consider-iterating-dictionary,
"""
+=======
+
+[tool.pylint.master]
+ignore=[".venv", "tests"]
+
+[tool.pylint.basic]
+# No docstrings required for private methods (Pylint default), or for test_ functions, or for inner Meta classes.
+no-docstring-rgx = "^(_|test_|Meta$)"
+
+
+[tool.pylint.messages_control]
+disable = [
+ "line-too-long",
+ "duplicate-code",
+ "cyclic-import",
+ ]
+>>>>>>> 5806227 (Cookie initialy baked by NetworkToCode Cookie Drift Manager Tool)
[tool.pylint.miscellaneous]
# Don't flag TODO as a failure, let us commit with things that still need to be done in the code
@@ -120,11 +209,16 @@ notes = """,
"""
[build-system]
+<<<<<<< HEAD
requires = ["poetry-core>=1.0.0"]
+=======
+requires = ["poetry_core>=1.0.0"]
+>>>>>>> 5806227 (Cookie initialy baked by NetworkToCode Cookie Drift Manager Tool)
build-backend = "poetry.core.masonry.api"
[tool.pytest.ini_options]
python_paths = "./"
+<<<<<<< HEAD
testpaths = "tests/"
addopts = "-vv --doctest-modules -p no:warnings --ignore-glob='*mock*'"
@@ -150,6 +244,13 @@ strict_equality = true
exclude = ["tests/", "tasks.py"]
show_error_codes = true
+=======
+testpaths = [
+ "tests/"
+]
+addopts = "-vv --doctest-modules -p no:warnings --ignore-glob='*mock*'"
+
+>>>>>>> 5806227 (Cookie initialy baked by NetworkToCode Cookie Drift Manager Tool)
[tool.towncrier]
package = "netutils"
directory = "changes"
diff --git a/tasks.py b/tasks.py
index 87ea81d5..fc705cc7 100644
--- a/tasks.py
+++ b/tasks.py
@@ -1,5 +1,9 @@
"""Tasks for use with Invoke."""
+<<<<<<< HEAD
+=======
+import os
+>>>>>>> 5806227 (Cookie initialy baked by NetworkToCode Cookie Drift Manager Tool)
import re
from pathlib import Path
@@ -10,8 +14,12 @@
def is_truthy(arg):
"""Convert "truthy" strings into Booleans.
+<<<<<<< HEAD
Examples
--------
+=======
+ Examples:
+>>>>>>> 5806227 (Cookie initialy baked by NetworkToCode Cookie Drift Manager Tool)
>>> is_truthy('yes')
True
Args:
@@ -30,17 +38,29 @@ def is_truthy(arg):
# Use pyinvoke configuration for default values, see http://docs.pyinvoke.org/en/stable/concepts/configuration.html
+<<<<<<< HEAD
# Variables may be overwritten in invoke.yml or by the environment variables INVOKE_PYNTC_xxx
+=======
+# Variables may be overwritten in invoke.yml or by the environment variables INVOKE_NETUTILS_xxx
+>>>>>>> 5806227 (Cookie initialy baked by NetworkToCode Cookie Drift Manager Tool)
namespace = Collection("netutils")
namespace.configure(
{
"netutils": {
"project_name": "netutils",
+<<<<<<< HEAD
"python_ver": "3.13",
"local": False,
"image_name": "netutils",
"image_ver": "latest",
"pwd": ".",
+=======
+ "python_ver": "3.8",
+ "local": is_truthy(os.getenv("INVOKE_NETUTILS_IMAGE_NAME", "false")),
+ "image_name": "netutils",
+ "image_ver": os.getenv("INVOKE_PARSER_IMAGE_VER", "latest"),
+ "pwd": Path(__file__).parent,
+>>>>>>> 5806227 (Cookie initialy baked by NetworkToCode Cookie Drift Manager Tool)
}
}
)
@@ -98,6 +118,12 @@ def run_command(context, exec_cmd, port=None):
return result
+<<<<<<< HEAD
+=======
+# ------------------------------------------------------------------------------
+# BUILD
+# ------------------------------------------------------------------------------
+>>>>>>> 5806227 (Cookie initialy baked by NetworkToCode Cookie Drift Manager Tool)
@task(
help={
"cache": "Whether to use Docker's cache when building images (default enabled)",
@@ -123,9 +149,37 @@ def build(context, cache=True, force_rm=False, hide=False):
@task
+<<<<<<< HEAD
def clean(context):
"""Remove the project specific image."""
print(f"Attempting to forcefully remove image {context.netutils.image_name}:{context.netutils.image_ver}")
+=======
+def generate_packages(context):
+ """Generate all Python packages inside docker and copy the file locally under dist/."""
+ command = "poetry build"
+ run_command(context, command)
+
+
+@task(
+ help={
+ "check": (
+ "If enabled, check for outdated dependencies in the poetry.lock file, "
+ "instead of generating a new one. (default: disabled)"
+ )
+ }
+)
+def lock(context, check=False):
+ """Generate poetry.lock inside the library container."""
+ run_command(context, f"poetry {'check' if check else 'lock --no-update'}")
+
+
+@task
+def clean(context):
+ """Remove the project specific image."""
+ print(
+ f"Attempting to forcefully remove image {context.netutils.image_name}:{context.netutils.image_ver}"
+ )
+>>>>>>> 5806227 (Cookie initialy baked by NetworkToCode Cookie Drift Manager Tool)
context.run(f"docker rmi {context.netutils.image_name}:{context.netutils.image_ver} --force")
print(f"Successfully removed image {context.netutils.image_name}:{context.netutils.image_ver}")
@@ -138,6 +192,7 @@ def rebuild(context):
@task
+<<<<<<< HEAD
def coverage(context):
"""Run the coverage report against pytest.
@@ -158,6 +213,11 @@ def pytest(context):
context (obj): Used to run specific commands
"""
exec_cmd = "pytest -vv --doctest-modules netutils/ && coverage run --source=netutils -m pytest && coverage report"
+=======
+def pytest(context, args=""):
+ """Run pytest test cases."""
+ exec_cmd = f"pytest {args}"
+>>>>>>> 5806227 (Cookie initialy baked by NetworkToCode Cookie Drift Manager Tool)
run_command(context, exec_cmd)
@@ -207,6 +267,7 @@ def ruff(context, action=None, target=None, fix=False, output_format="concise"):
@task
+<<<<<<< HEAD
def mypy(context):
"""Run mypy to validate typing-hints.
@@ -219,14 +280,21 @@ def mypy(context):
@task
+=======
+>>>>>>> 5806227 (Cookie initialy baked by NetworkToCode Cookie Drift Manager Tool)
def pylint(context):
"""Run pylint for the specified name and Python version.
Args:
context (obj): Used to run specific commands
+<<<<<<< HEAD
local (bool): Define as `True` to execute locally
"""
exec_cmd = 'find . -name "*.py" | grep -vE "(tests/unit/mock|netutils/data_files)" | xargs pylint'
+=======
+ """
+ exec_cmd = 'find . -name "*.py" | grep -vE "tests/unit" | xargs pylint'
+>>>>>>> 5806227 (Cookie initialy baked by NetworkToCode Cookie Drift Manager Tool)
run_command(context, exec_cmd)
@@ -236,7 +304,10 @@ def yamllint(context):
Args:
context (obj): Used to run specific commands
+<<<<<<< HEAD
local (bool): Define as `True` to execute locally
+=======
+>>>>>>> 5806227 (Cookie initialy baked by NetworkToCode Cookie Drift Manager Tool)
"""
exec_cmd = "yamllint ."
run_command(context, exec_cmd)
@@ -253,12 +324,22 @@ def cli(context):
context.run(f"{dev}", pty=True)
+<<<<<<< HEAD
@task
def tests(context):
+=======
+@task(
+ help={
+ "lint-only": "Only run linters; unit tests will be excluded. (default: False)",
+ }
+)
+def tests(context, lint_only=False):
+>>>>>>> 5806227 (Cookie initialy baked by NetworkToCode Cookie Drift Manager Tool)
"""Run all tests for the specified name and Python version.
Args:
context (obj): Used to run specific commands
+<<<<<<< HEAD
"""
ruff(context)
pylint(context)
@@ -266,12 +347,35 @@ def tests(context):
mypy(context)
pytest(context)
+=======
+ lint_only (bool): If True, only run linters and skip unit tests.
+ """
+ # If we are not running locally, start the docker containers so we don't have to for each test
+ # Sorted loosely from fastest to slowest
+ print("Running ruff...")
+ ruff(context)
+ print("Running yamllint...")
+ yamllint(context)
+ print("Running poetry check...")
+ lock(context, check=True)
+ print("Running pylint...")
+ pylint(context)
+ print("Running mkdocs...")
+ build_and_check_docs(context)
+ if not lint_only:
+ print("Running unit tests...")
+ pytest(context)
+>>>>>>> 5806227 (Cookie initialy baked by NetworkToCode Cookie Drift Manager Tool)
print("All tests have passed!")
@task
def build_and_check_docs(context):
+<<<<<<< HEAD
"""Build documentation to be available within Docs Sites."""
+=======
+ """Build documentation and test the configuration."""
+>>>>>>> 5806227 (Cookie initialy baked by NetworkToCode Cookie Drift Manager Tool)
command = "mkdocs build --no-directory-urls --strict"
run_command(context, command)
@@ -290,7 +394,11 @@ def build_and_check_docs(context):
@task
def docs(context):
"""Build and serve docs locally for development."""
+<<<<<<< HEAD
exec_cmd = "mkdocs serve -v --dev-addr=0.0.0.0:8001"
+=======
+ exec_cmd = "mkdocs serve -v"
+>>>>>>> 5806227 (Cookie initialy baked by NetworkToCode Cookie Drift Manager Tool)
run_command(context, exec_cmd, port="8001:8001")
diff --git a/tests/integration/__init__.py b/tests/integration/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py
index de331952..b42742ec 100644
--- a/tests/unit/conftest.py
+++ b/tests/unit/conftest.py
@@ -1,5 +1,6 @@
"""Used to setup fixtures to be used through tests"""
+<<<<<<< HEAD
import importlib.util
import json
import os
@@ -68,3 +69,13 @@ def _method(_file, attr):
return getattr(module, attr)
return _method
+=======
+import pytest
+from click.testing import CliRunner
+
+
+@pytest.fixture
+def cli_runner():
+ """Provide CLI runner for Click tests."""
+ return CliRunner()
+>>>>>>> 5806227 (Cookie initialy baked by NetworkToCode Cookie Drift Manager Tool)
diff --git a/tests/unit/test_basic.py b/tests/unit/test_basic.py
new file mode 100644
index 00000000..1cfe63c5
--- /dev/null
+++ b/tests/unit/test_basic.py
@@ -0,0 +1,48 @@
+"""Basic tests that do not require Django."""
+
+# import os
+# import re
+# import unittest
+
+# import toml
+
+
+# class TestDocsPackaging(unittest.TestCase):
+# """Test Version in doc requirements is the same pyproject."""
+
+# def test_version(self):
+# """Verify that pyproject.toml dev dependencies have the same versions as in the docs requirements.txt."""
+# parent_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
+# poetry_path = os.path.join(parent_path, "pyproject.toml")
+# poetry_details = toml.load(poetry_path)["tool"]["poetry"]["group"]["dev"]["dependencies"]
+# with open(f"{parent_path}/docs/requirements.txt", "r", encoding="utf-8") as file:
+# requirements = [line for line in file.read().splitlines() if (len(line) > 0 and not line.startswith("#"))]
+# for pkg in requirements:
+# package_name = pkg
+# if len(pkg.split("==")) == 2: # noqa: PLR2004
+# package_name, version = pkg.split("==")
+# else:
+# version = "*"
+# self.assertEqual(poetry_details[package_name], version)
+
+
+# class TestDocsReleaseNotes(unittest.TestCase):
+# """Test that mkdocs has the release notes for the current version."""
+
+# def test_version_file_found(self):
+# """Verify that if the current version has no letters, which would see in alpha or beta has an associated release note file."""
+# parent_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
+# poetry_path = os.path.join(parent_path, "pyproject.toml")
+# project_version = toml.load(poetry_path)["tool"]["poetry"]["version"]
+
+# docs_path = os.path.join(parent_path, "docs")
+# release_notes_files = [file for file in os.listdir(f"{docs_path}/admin/release_notes/") if file.endswith(".md")]
+# version_pattern = re.compile(r"^(\d+)\.(\d+)\.\d+$")
+
+# match = version_pattern.match(project_version)
+# # If there is no match, then it is likely an alpha or beta version and we can skip this test.
+# if match:
+# major, minor = match.groups()
+# version_str = f"version_{major}.{minor}.md"
+# if version_str not in release_notes_files:
+# self.fail(f"Release note file for version {version_str} not found in release notes folder.")
diff --git a/tests/unit/test_cli.py b/tests/unit/test_cli.py
new file mode 100644
index 00000000..b743798a
--- /dev/null
+++ b/tests/unit/test_cli.py
@@ -0,0 +1,16 @@
+"""Example Test using Fixtures."""
+
+import mock
+
+from netutils import cli
+
+
+@mock.patch("netutils.cli.log")
+def test_cli_logging(log, cli_runner):
+ """Assert our logging gets called in CLI app."""
+ result = cli_runner.invoke(cli.main, ["--test", "ntc"])
+
+ assert result.exit_code == 0
+ assert result.output == "ntc\n"
+ log.info.assert_called()
+ log.info.assert_called_with("Entrypoint of the CLI app.")
diff --git a/tests/unit/test_logging.py b/tests/unit/test_logging.py
new file mode 100644
index 00000000..a05cf26e
--- /dev/null
+++ b/tests/unit/test_logging.py
@@ -0,0 +1,58 @@
+"""Validate netutils logging works."""
+
+import mock
+
+import netutils
+
+
+@mock.patch("logging.config.dictConfig")
+@mock.patch("logging.getLogger")
+def test_initialize_logging_default(get_logger, basic_cfg):
+ """Test initialize_logging using defaults."""
+ netutils.log.initialize_logging()
+
+ basic_cfg.assert_called_once()
+ initial_call = basic_cfg.mock_calls[0].args[0]
+ assert set(initial_call.keys()) == set(["version", "disable_existing_loggers", "formatters", "handlers", "loggers"])
+ assert initial_call["handlers"]["standard"]["level"] == "INFO"
+
+ get_logger.assert_called_once()
+ assert get_logger.mock_calls[0].args == ("netutils",)
+ assert get_logger.mock_calls[1].args == ("Logging initialized.",)
+
+
+@mock.patch("logging.config.dictConfig")
+@mock.patch("logging.getLogger")
+def test_initialize_logging_user_defined_config(get_logger, basic_cfg):
+ """Test initialize_logging with user defined config."""
+ config = {"version": 1, "disable_existing_loggers": False}
+ netutils.log.initialize_logging(config=config)
+
+ basic_cfg.assert_called_once()
+ initial_call = basic_cfg.mock_calls[0].args[0]
+ assert initial_call == config
+
+ get_logger.assert_called_once()
+ assert get_logger.mock_calls[0].args == ("netutils",)
+ assert get_logger.mock_calls[1].args == ("Logging initialized.",)
+
+
+@mock.patch("logging.config.dictConfig")
+@mock.patch("logging.getLogger")
+def test_initialize_logging_filename(get_logger, basic_cfg):
+ """Test initialize_logging with filename."""
+ netutils.log.initialize_logging(filename="output.log")
+
+ basic_cfg.assert_called_once()
+ initial_call = basic_cfg.mock_calls[0].args[0]
+ assert set(initial_call.keys()) == set(["version", "disable_existing_loggers", "formatters", "handlers", "loggers"])
+ assert initial_call["handlers"]["standard"]["level"] == "INFO"
+ assert initial_call["handlers"]["file_output"]["filename"] == "output.log"
+ assert initial_call["handlers"]["file_output"]["level"] == "DEBUG"
+ assert initial_call["handlers"]["file_output"]["formatter"] == "debug"
+ assert initial_call["handlers"]["file_output"]["class"] == "logging.FileHandler"
+ assert "file_output" in initial_call["loggers"][""]["handlers"]
+
+ get_logger.assert_called_once()
+ assert get_logger.mock_calls[0].args == ("netutils",)
+ assert get_logger.mock_calls[1].args == ("Logging initialized.",)
diff --git a/towncrier_template.j2 b/towncrier_template.j2
index 2c1316f5..83387657 100644
--- a/towncrier_template.j2
+++ b/towncrier_template.j2
@@ -40,3 +40,7 @@ No significant changes.
{% endif %}
{% endfor %}
+<<<<<<< HEAD
+=======
+
+>>>>>>> 5806227 (Cookie initialy baked by NetworkToCode Cookie Drift Manager Tool)