Skip to content

feat: Setting up Ruff and updating the repo content accordingly #594

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions .flake8

This file was deleted.

2 changes: 1 addition & 1 deletion .github/labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ documentation:
- any-glob-to-any-file: ['doc/source/**/*']
maintenance:
- changed-files:
- any-glob-to-any-file: ['.github/**/*', '.flake8', 'pyproject.toml', 'docker/**/*']
- any-glob-to-any-file: ['.github/**/*', 'ruff.toml', 'pyproject.toml', 'docker/**/*']
dependencies:
- changed-files:
- any-glob-to-any-file: ['requirements/*']
26 changes: 4 additions & 22 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,36 +1,18 @@
repos:


- repo: https://github.com/psf/black
rev: 25.1.0 # IF VERSION CHANGES --> MODIFY "blacken-docs" MANUALLY AS WELL!!
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.11.8
hooks:
- id: black
args: [
"doc/source/conf.py",
"examples"
]
- id: ruff
- id: ruff-format

- repo: https://github.com/adamchainz/blacken-docs
rev: 1.19.1
hooks:
- id: blacken-docs
additional_dependencies: [black==24.8.0]

- repo: https://github.com/pycqa/isort
rev: 6.0.1
hooks:
- id: isort
args: [
"--profile", "black",
"--force-sort-within-sections",
"--line-length", "100",
]

- repo: https://github.com/PyCQA/flake8
rev: 7.2.0
hooks:
- id: flake8

- repo: https://github.com/codespell-project/codespell
rev: v2.4.1
hooks:
Expand Down
4 changes: 2 additions & 2 deletions doc/source/doc-style/code/sample_func.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
def func(arg1, arg2):
def func(arg1, arg2): # noqa: D100
"""Summary line <should be only one line>.

Extended description of the function. The extended description,
Expand All @@ -25,7 +25,7 @@ def func(arg1, arg2):

Examples
--------
>>> func(1, 'foo')
>>> func(1, "foo")
True

"""
Expand Down
39 changes: 15 additions & 24 deletions doc/source/how-to/code/pyansys_logging.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""Module for PyAnsys logging."""

from copy import copy
from datetime import datetime
import logging
Expand All @@ -10,9 +12,7 @@


# Formatting
STDOUT_MSG_FORMAT = (
"%(levelname)s - %(instance_name)s - %(module)s - %(funcName)s - %(message)s"
)
STDOUT_MSG_FORMAT = "%(levelname)s - %(instance_name)s - %(module)s - %(funcName)s - %(message)s"
FILE_MSG_FORMAT = STDOUT_MSG_FORMAT

DEFAULT_STDOUT_HEADER = """
Expand Down Expand Up @@ -59,6 +59,7 @@ def __init__(self, logger, extra=None):
self.std_out_handler = logger.std_out_handler

def process(self, msg, kwargs):
"""Get instance_name for logging."""
kwargs["extra"] = {}
# These are the extra parameters sent to log
# here self.extra is the argument pass to the log records.
Expand All @@ -77,7 +78,6 @@ def log_to_file(self, filename=FILE_NAME, level=LOG_LEVEL):
Level of logging, for example ``'DEBUG'``. By default
``logging.DEBUG``.
"""

self.logger = add_file_handler(
self.logger, filename=filename, level=level, write_headers=True
)
Expand Down Expand Up @@ -106,6 +106,8 @@ def setLevel(self, level="DEBUG"):


class PyAnsysPercentStyle(logging.PercentStyle):
"""Log message formatting."""

def __init__(self, fmt, *, defaults=None):
self._fmt = fmt or self.default_format
self._defaults = defaults
Expand Down Expand Up @@ -154,6 +156,7 @@ class InstanceFilter(logging.Filter):
"""Ensures that instance_name record always exists."""

def filter(self, record):
"""If record had no attribute instance_name, create it and populate with empty string."""
if not hasattr(record, "instance_name"):
record.instance_name = ""
return True
Expand Down Expand Up @@ -193,14 +196,11 @@ def __init__(
cleanup=True,
):
"""Initialize Logger class."""

self.logger = logging.getLogger(
"pyproject_global"
) # Creating default main logger.
self.logger = logging.getLogger("pyproject_global") # Creating default main logger.
self.logger.addFilter(InstanceFilter())
self.logger.setLevel(level)
self.logger.propagate = True
self.level = self.logger.level # TODO: TO REMOVE
self.level = self.logger.level # noqa: TD002, TD003 # TODO: TO REMOVE

# Writing logging methods.
self.debug = self.logger.debug
Expand Down Expand Up @@ -233,10 +233,7 @@ def log_to_file(self, filename=FILE_NAME, level=LOG_LEVEL):
level : str, optional
Level of logging. E.x. 'DEBUG'. By default LOG_LEVEL
"""

self = add_file_handler(
self, filename=filename, level=level, write_headers=True
)
self = add_file_handler(self, filename=filename, level=level, write_headers=True)

def log_to_stdout(self, level=LOG_LEVEL):
"""Add standard output handler to the logger.
Expand All @@ -246,7 +243,6 @@ def log_to_stdout(self, level=LOG_LEVEL):
level : str, optional
Level of logging record. By default LOG_LEVEL
"""

self = add_stdout_handler(self, level=level)

def setLevel(self, level="DEBUG"):
Expand Down Expand Up @@ -333,9 +329,7 @@ def _add_product_instance_logger(self, name, product_instance, level):
self._make_child_logger("NO_NAMED_YET", level), product_instance
)
else:
raise TypeError(
f"``name`` parameter must be a string or None, not f{type(name)}"
)
raise TypeError(f"``name`` parameter must be a string or None, not f{type(name)}")

return instance_logger

Expand Down Expand Up @@ -379,21 +373,20 @@ def add_instance_logger(self, name, product_instance, level=None):
return self._instances[new_name]

def __getitem__(self, key):
"""Define custom KeyError message."""
if key in self._instances.keys():
return self._instances[key]
else:
raise KeyError(f"There are no instances with name {key}")

def add_handling_uncaught_expections(self, logger):
"""This just redirects the output of an exception to the logger."""
"""Redirect the output of an exception to the logger."""

def handle_exception(exc_type, exc_value, exc_traceback):
if issubclass(exc_type, KeyboardInterrupt):
sys.__excepthook__(exc_type, exc_value, exc_traceback)
return
logger.critical(
"Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback)
)
logger.critical("Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback))

sys.excepthook = handle_exception

Expand All @@ -405,7 +398,7 @@ def __del__(self):
for handler in self.logger.handlers:
handler.close()
self.logger.removeHandler(handler)
except Exception as e:
except Exception:
try:
if self.logger is not None:
self.logger.error("The logger was not deleted properly.")
Expand Down Expand Up @@ -434,7 +427,6 @@ def add_file_handler(logger, filename=FILE_NAME, level=LOG_LEVEL, write_headers=
logger
Return the logger or Logger object.
"""

file_handler = logging.FileHandler(filename)
file_handler.setLevel(level)
file_handler.setFormatter(logging.Formatter(FILE_MSG_FORMAT))
Expand Down Expand Up @@ -471,7 +463,6 @@ def add_stdout_handler(logger, level=LOG_LEVEL, write_headers=False):
logger
The logger or Logger object.
"""

std_out_handler = logging.StreamHandler(sys.stdout)
std_out_handler.setLevel(level)
std_out_handler.setFormatter(PyProjectFormatter(STDOUT_MSG_FORMAT))
Expand Down
33 changes: 15 additions & 18 deletions doc/source/how-to/code/test_pyansys_logging.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
"""Test for PyAnsys logging."""

import io
import logging
import os
from pathlib import Path
import sys
import weakref

Expand All @@ -9,25 +11,24 @@

def test_default_logger():
"""Create a logger with default options.
Only stdout logger must be used."""

Only stdout logger must be used.
"""
capture = CaptureStdOut()
with capture:
test_logger = pyansys_logging.Logger()
test_logger.info("Test stdout")

assert (
"INFO - - test_pyansys_logging - test_default_logger - Test stdout"
in capture.content
)
assert "INFO - - test_pyansys_logging - test_default_logger - Test stdout" in capture.content
# File handlers are not activated.
assert os.path.exists(os.path.exists(os.path.join(os.getcwd(), "PyProject.log")))
assert Path.exists(Path.exists(Path(Path.cwd() / "PyProject.log")))


def test_level_stdout():
"""Create a logger with default options.
Only stdout logger must be used."""

Only stdout logger must be used.
"""
capture = CaptureStdOut()
with capture:
test_logger = pyansys_logging.Logger(level=logging.INFO)
Expand Down Expand Up @@ -85,34 +86,28 @@ def test_level_stdout():
)

# File handlers are not activated.
assert os.path.exists(os.path.exists(os.path.join(os.getcwd(), "PyProject.log")))
assert (Path.cwd() / "PyProject.log").exists()


def test_file_handlers(tmpdir):
"""Activate a file handler different from `PyProject.log`."""

file_logger = tmpdir.mkdir("sub").join("test_logger.txt")

test_logger = pyansys_logging.Logger(to_file=True, filename=file_logger)
test_logger.info("Test Misc File")

with open(file_logger, "r") as f:
with Path.open(file_logger, "r") as f:
content = f.readlines()

assert os.path.exists(
file_logger
) # The file handler is not the default PyProject.Log
assert Path.exists(file_logger) # The file handler is not the default PyProject.Log
assert len(content) == 6
assert "NEW SESSION" in content[2]
assert (
"==============================================================================="
in content[3]
)
assert "LEVEL - INSTANCE NAME - MODULE - FUNCTION - MESSAGE" in content[4]
assert (
"INFO - - test_pyansys_logging - test_file_handlers - Test Misc File"
in content[5]
)
assert "INFO - - test_pyansys_logging - test_file_handlers - Test Misc File" in content[5]

# Delete the logger and its file handler.
test_logger_ref = weakref.ref(test_logger)
Expand All @@ -127,9 +122,11 @@ def __init__(self):
self._stream = io.StringIO()

def __enter__(self):
"""Runtime context is entered."""
sys.stdout = self._stream

def __exit__(self, type, value, traceback):
"""Runtime context is exited."""
sys.stdout = sys.__stdout__

@property
Expand Down
1 change: 1 addition & 0 deletions examples/pyvista_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

# ruff: noqa: D400
"""
.. _adding_a_new_gallery_example:

Expand Down
37 changes: 37 additions & 0 deletions ruff.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
exclude = [
"build",
"doc/source/conf.py",
]

line-length = 100

[format]
quote-style = "double"
indent-style = "space"
docstring-code-format = true

[lint]
select = [
"D", # pydocstyle, see https://docs.astral.sh/ruff/rules/#pydocstyle-d
"E", # pycodestyle, see https://docs.astral.sh/ruff/rules/#pycodestyle-e-w
"F", # pyflakes, see https://docs.astral.sh/ruff/rules/#pyflakes-f
"I", # isort, see https://docs.astral.sh/ruff/rules/#isort-i
"N", # pep8-naming, see https://docs.astral.sh/ruff/rules/#pep8-naming-n
"PTH", # flake8-use-pathlib, https://docs.astral.sh/ruff/rules/#flake8-use-pathlib-pth
"TD", # flake8-todos, https://docs.astral.sh/ruff/rules/#flake8-todos-td
"W", # pycodestyle, see https://docs.astral.sh/ruff/rules/#pycodestyle-e-w
]
ignore = []

[lint.pydocstyle]
convention = "numpy"

[lint.isort]
combine-as-imports = true
force-sort-within-sections = true

[lint.mccabe]
max-complexity = 10

[lint.pep8-naming]
ignore-names = ["setLevel"]