Skip to content
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

Logger implementation and tests #265

Merged
merged 59 commits into from Jun 13, 2023
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
4cb61be
Logger implementation and tests
koubaa Jun 8, 2023
f895975
return code hack for linux
koubaa Jun 8, 2023
9e937e0
doc
koubaa Jun 8, 2023
3ccd8fd
Merge remote-tracking branch 'origin/main' into fix-embedding-logger
koubaa Jun 8, 2023
5e4c1fe
fix doc
koubaa Jun 8, 2023
89d4503
linux api
koubaa Jun 8, 2023
66cadaf
style
koubaa Jun 8, 2023
7670dc5
fix success check
koubaa Jun 8, 2023
f97e88f
Update doc/source/user_guide_embedding/index.rst
koubaa Jun 9, 2023
955edae
Update src/ansys/mechanical/core/embedding/logger/__init__.py
koubaa Jun 9, 2023
0c7caea
update assertion
koubaa Jun 12, 2023
68bb6a7
Merge branch 'fix-embedding-logger' of https://github.com/ansys/pymec…
koubaa Jun 12, 2023
9a912ec
fast CI turnaround & fix codestyle
koubaa Jun 12, 2023
ece85b1
recover ci/cd
Jun 12, 2023
7ccb530
Update doc/source/examples/pymechanical_examples_repo/index.rst
koubaa Jun 12, 2023
81e9a92
Update doc/source/examples/pymechanical_examples_repo/index.rst
koubaa Jun 12, 2023
4706c86
Update doc/source/user_guide_embedding/logging.rst
koubaa Jun 12, 2023
72e567a
Update doc/source/user_guide_embedding/logging.rst
koubaa Jun 12, 2023
e2d8989
Update doc/source/user_guide_embedding/logging.rst
koubaa Jun 12, 2023
5960a69
Update src/ansys/mechanical/core/embedding/logger/__init__.py
koubaa Jun 12, 2023
d2fce95
Update src/ansys/mechanical/core/embedding/logger/__init__.py
koubaa Jun 12, 2023
a2981d9
Update doc/source/user_guide_embedding/logging.rst
koubaa Jun 12, 2023
3564a9c
Update doc/source/user_guide_embedding/index.rst
koubaa Jun 12, 2023
a090b5d
Update src/ansys/mechanical/core/embedding/logger/sinks.py
koubaa Jun 12, 2023
152257a
Update tests/test_mechanical.py
koubaa Jun 12, 2023
c69f3c7
Update tests/embedding/test_app.py
koubaa Jun 12, 2023
464ae39
Update tests/conftest.py
koubaa Jun 12, 2023
b507274
clarify docstring
Jun 12, 2023
8d11519
Update tests/conftest.py
koubaa Jun 12, 2023
8dd4f19
Update src/ansys/mechanical/core/embedding/logger/windows_api.py
koubaa Jun 12, 2023
c2b3071
Update src/ansys/mechanical/core/embedding/logger/windows_api.py
koubaa Jun 12, 2023
6a7c231
Update src/ansys/mechanical/core/embedding/logger/windows_api.py
koubaa Jun 12, 2023
50cbefb
Update doc/source/user_guide_embedding/logging.rst
koubaa Jun 12, 2023
aea1d79
Update src/ansys/mechanical/core/embedding/logger/__init__.py
koubaa Jun 12, 2023
f40e60e
sentences
Jun 12, 2023
643d68d
Merge branch 'fix-embedding-logger' of https://github.com/ansys/pymec…
Jun 12, 2023
3f25cd4
Update src/ansys/mechanical/core/embedding/logger/__init__.py
koubaa Jun 12, 2023
7de78e9
Update src/ansys/mechanical/core/embedding/logger/__init__.py
koubaa Jun 12, 2023
e2f5041
Update src/ansys/mechanical/core/embedding/logger/__init__.py
koubaa Jun 12, 2023
ae139db
Update src/ansys/mechanical/core/embedding/logger/__init__.py
koubaa Jun 12, 2023
f5069ba
Update src/ansys/mechanical/core/embedding/logger/__init__.py
koubaa Jun 12, 2023
4dd3d21
Update src/ansys/mechanical/core/embedding/logger/environ.py
koubaa Jun 12, 2023
66c66c7
wording
Jun 12, 2023
cf50fbd
Update src/ansys/mechanical/core/embedding/logger/__init__.py
koubaa Jun 12, 2023
3323cd1
Update src/ansys/mechanical/core/embedding/logger/__init__.py
koubaa Jun 12, 2023
d404f61
Update src/ansys/mechanical/core/embedding/logger/__init__.py
koubaa Jun 12, 2023
55832f8
taketh away the givens
Jun 12, 2023
44fef61
Update src/ansys/mechanical/core/embedding/logger/environ.py
koubaa Jun 12, 2023
aaf76f9
Apply suggestions from code review
koubaa Jun 12, 2023
42c7e4c
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 12, 2023
88f1c20
let this sink in
Jun 12, 2023
e516ec6
try
Jun 12, 2023
bc1507b
debug
Jun 12, 2023
d8aa019
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 12, 2023
a9cb58f
try
Jun 12, 2023
19243d1
Merge branch 'fix-embedding-logger' of https://github.com/ansys/pymec…
Jun 12, 2023
db56010
test
Jun 12, 2023
217a878
Merge branch 'main' into fix-embedding-logger
RobPasMue Jun 13, 2023
5db20a2
style
Jun 13, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/ci_cd.yml
Expand Up @@ -172,6 +172,8 @@ jobs:
ANSYSCL232_DIR: /install/ansys_inc/v232/licensingclient
ANSYSLMD_LICENSE_FILE: 1055@${{ secrets.LICENSE_SERVER }}
ANSYS_WORKBENCH_LOGGING_CONSOLE: 0
ANSYS_WORKBENCH_LOGGING: 0
ANSYS_WORKBENCH_LOGGING_FILTER_LEVEL: 2
NUM_CORES: 1
run: |
/install/ansys_inc/v232/aisol/.workbench_lite pytest -m embedding > pytest_output.txt || true
Expand Down
8 changes: 4 additions & 4 deletions doc/source/examples/pymechanical_examples_repo/index.rst
@@ -1,19 +1,19 @@
.. _ref_pymechanical_examples_repository:

PyMechanical remote sessions examples repository
PyMechanical remote session examples repository
================================================

Remote sessions examples are hosted in the `PyMechanical Examples repository <pymechanical_remote_ex_repo_>`_.
Remote session examples are hosted in the `PyMechanical Examples repository <pymechanical_remote_ex_repo_>`_.

The documentation for these examples can be found in the
`PyMechanical Examples documentation <pymechanical_remote_ex_doc_>`_.

.. === REMOTE SESSIONS EXAMPLES ===

Remote Sessions examples
Remote session examples
------------------------

Remote sessions examples demonstrate the basic simulation capabilities of Mechanical using remote sessions.
Remote session examples demonstrate the basic simulation capabilities of Mechanical using remote sessions.
koubaa marked this conversation as resolved.
Show resolved Hide resolved

The documentation to these examples can be found in the
`Remote sessions examples <pymechanical_remote_ex_all_>`_ section.
koubaa marked this conversation as resolved.
Show resolved Hide resolved
6 changes: 6 additions & 0 deletions doc/source/user_guide_embedding/index.rst
Expand Up @@ -17,6 +17,7 @@ an instance of Mechanical in Python.

configuration
globals
logging


Overview
Expand Down Expand Up @@ -50,6 +51,11 @@ By default, an instance of the :class:`Application <ansys.mechanical.core.embedd
is configured in the same way as Mechanical. To customize an instance, see
:ref:`ref_embedding_user_guide_configuration`.

Diagnosing problems with embedding
----------------------------------
In some cases, debugging why the embedded Mechanical instance is not working requires additional logging.
See the :ref:`ref_embedding_user_guide_logging` for instructions on how to configure logging.
koubaa marked this conversation as resolved.
Show resolved Hide resolved

Running PyMechanical embedding scripts inside Mechanical with IronPython
------------------------------------------------------------------------
If your PyMechanical embedding script does not use any other third-party Python package, such as `NumPy`,
koubaa marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
31 changes: 31 additions & 0 deletions doc/source/user_guide_embedding/logging.rst
@@ -0,0 +1,31 @@
.. _ref_embedding_user_guide_logging:

*******
Logging
*******

Mechanical has a logging system that is useful when debugging issues. Normally, it is
enabled by setting environment variables before starting Mechanical. With PyMechanical,
it is possible to configure logging at any time, whether it is before or after creating
the embedded Application, using the same Python API.
koubaa marked this conversation as resolved.
Show resolved Hide resolved

Use the :class:`Configuration <ansys.mechanical.core.embedding.logger.Configuration>` to
koubaa marked this conversation as resolved.
Show resolved Hide resolved
configure logging to the stdout for all warning messages and above(that is error and fatal).
koubaa marked this conversation as resolved.
Show resolved Hide resolved
For example:

.. code:: python

import ansys.mechanical.core as mech
from ansys.mechanical.core.embedding.logger import Configuration, Logger

Configuration.configure(level=logging.WARNING, to_stdout=True)
_ = mech.App()

After the embedded application has been created, you can write messages to the this same
koubaa marked this conversation as resolved.
Show resolved Hide resolved
log using the :class:`Logger <ansys.mechanical.core.embedding.logger.Logger>` like this:
koubaa marked this conversation as resolved.
Show resolved Hide resolved

.. code:: python

from ansys.mechanical.core.embedding.logger import Logger

Logger.error("message")
1 change: 1 addition & 0 deletions doc/styles/Vocab/ANSYS/accept.txt
Expand Up @@ -26,6 +26,7 @@ Windows
windows
WSL
wsl
stdout
CPython
namespaces
Globals
Expand Down
1 change: 0 additions & 1 deletion src/ansys/mechanical/core/embedding/__init__.py
Expand Up @@ -2,4 +2,3 @@
from .app import App
from .config import Configuration
from .imports import global_variables
from .logging import Logging
3 changes: 1 addition & 2 deletions src/ansys/mechanical/core/embedding/app.py
Expand Up @@ -55,8 +55,7 @@ def __init__(self, db_file=None, **kwargs):
self._app = Ansys.Mechanical.Embedding.Application(db_file)
runtime.initialize(self._version)
self._disposed = False
if len(INSTANCES) == 0:
atexit.register(_dispose_embedded_app, INSTANCES)
atexit.register(_dispose_embedded_app, INSTANCES)
INSTANCES.append(self)

def __repr__(self):
Expand Down
3 changes: 2 additions & 1 deletion src/ansys/mechanical/core/embedding/initializer.py
Expand Up @@ -74,11 +74,12 @@ def initialize(version=None):
if INITIALIZED_VERSION != None:
assert INITIALIZED_VERSION == version
return
INITIALIZED_VERSION = version

if version == None:
version = _get_default_version()

INITIALIZED_VERSION = version

__disable_sec()

# need to add system path in order to import the assembly with the resolver
Expand Down
193 changes: 193 additions & 0 deletions src/ansys/mechanical/core/embedding/logger/__init__.py
@@ -0,0 +1,193 @@
"""Embedding logger.

Module to interact with the built-in logging system of Mechanical.

Usage
-----

Configuring logger
~~~~~~~~~~~~~~~~~~

Configuring the logger can be done using the Configuration class, for instance:
koubaa marked this conversation as resolved.
Show resolved Hide resolved

.. code:: python
import ansys.mechanical.core as mech
from ansys.mechanical.core.embedding.logger import Configuration, Logger

Configuration.configure(level=logging.INFO, to_stdout=True, base_directory=None)
app = mech.App(version=241)

Then, the Logger class can be used to write messages to the log, for instance:
koubaa marked this conversation as resolved.
Show resolved Hide resolved

.. code:: python

Logger.error("message")


"""

import logging
import os
import typing

from ansys.mechanical.core.embedding import initializer
from ansys.mechanical.core.embedding.logger import environ, linux_api, sinks, windows_api

LOGGING_SINKS: typing.Set[int] = set()
LOGGING_CONTEXT: str = "PYMECHANICAL"


def _get_backend() -> (
typing.Union[windows_api.APIBackend, linux_api.APIBackend, environ.EnvironBackend]
):
"""Get the appropriate logger backend.

Before embedding is initialized, logging is configured via environment variables
koubaa marked this conversation as resolved.
Show resolved Hide resolved
After embedding is initialized, logging is configured by making API calls into the
mechanical logging system.
koubaa marked this conversation as resolved.
Show resolved Hide resolved

However, the API is mostly the same in both cases, though some methods only work
in one of the two backends.

Setting the base directory only works before initializing
Actually logging a message or flushing the log only works after initializing
koubaa marked this conversation as resolved.
Show resolved Hide resolved
"""
# TODO - use abc instead of a union type?
embedding_initialized = initializer.INITIALIZED_VERSION != None
if not embedding_initialized:
return environ.EnvironBackend()
if os.name == "nt":
return windows_api.APIBackend()
return linux_api.APIBackend()


class Configuration:
"""Logger configuration for Mechanical embedding."""
koubaa marked this conversation as resolved.
Show resolved Hide resolved

@classmethod
def configure(cls, level=logging.WARNING, directory=None, base_directory=None, to_stdout=True):
"""Configure the logger for PyMechanical embedding.

Parameters
----------
level : int, optional
Level of logging that is defined in the ``logging`` package. The default is 'DEBUG'.
Options are ``"DEBUG"``, ``"INFO"``, ``"WARNING"``, and ``"ERROR"``.
directory : str, optional
Directory to write log file to. The default is ``None``, but by default the logs
will appear somewhere in the system temp folder.
koubaa marked this conversation as resolved.
Show resolved Hide resolved
base_directory: str, optional
Base directory to write log files to. This is only possible to set before the
Mechanical application is initialized.
koubaa marked this conversation as resolved.
Show resolved Hide resolved
to_stdout : bool, optional
Whether to write log messages to the standard output, which is the
command line. The default is ``True``.
"""
# Setup the global log configuration.
koubaa marked this conversation as resolved.
Show resolved Hide resolved
cls.set_log_directory(directory)
cls.set_log_base_directory(base_directory)

# Setup the sink-specific log configuration, store to global state.
koubaa marked this conversation as resolved.
Show resolved Hide resolved
cls._store_stdout_sink_enabled(to_stdout)
file_sink_enabled = directory != None or base_directory != None
cls._store_file_sink_enabled(file_sink_enabled)

# Commit the sink-specific log configuration global state to the backend.
cls._commit_enabled_configuration()
cls.set_log_level(level)

@classmethod
def set_log_to_stdout(cls, value: bool) -> None:
"""Configure logging to write to the stdout."""
koubaa marked this conversation as resolved.
Show resolved Hide resolved
cls._store_stdout_sink_enabled(value)
cls._commit_enabled_configuration()

@classmethod
def set_log_to_file(cls, value: bool) -> None:
"""Configure logging to write to a file."""
cls._store_file_sink_enabled(value)
cls._commit_enabled_configuration()

@classmethod
def set_log_level(cls, level: int) -> None:
"""Set the log level for all configured sinks."""
if len(LOGGING_SINKS) == 0:
raise Exception("No logging backend configured!")
cls._commit_level_configuration(level)

@classmethod
def set_log_directory(cls, value: str) -> None:
"""Configure logging to write to a directory."""
if value == None:
return
_get_backend().set_directory(value)

@classmethod
def set_log_base_directory(cls, value: str) -> None:
"""Configure logging to write in a time-stamped subfolder in the given directory."""
if value == None:
return
_get_backend().set_base_directory(value)

@classmethod
def _commit_level_configuration(cls, level: int) -> None:
for sink in LOGGING_SINKS:
_get_backend().set_log_level(level, sink)

@classmethod
def _commit_enabled_configuration(cls) -> None:
for sink in LOGGING_SINKS:
_get_backend().enable(sink)

@classmethod
def _store_stdout_sink_enabled(cls, value: bool) -> None:
if value:
LOGGING_SINKS.add(sinks.StandardSinks.CONSOLE)
else:
LOGGING_SINKS.discard(sinks.StandardSinks.CONSOLE)

@classmethod
def _store_file_sink_enabled(cls, value: bool) -> None:
if value:
LOGGING_SINKS.add(sinks.StandardSinks.STANDARD_LOG_FILE)
else:
LOGGING_SINKS.discard(sinks.StandardSinks.STANDARD_LOG_FILE)


class Logger:
"""Logger class for embedding."""
koubaa marked this conversation as resolved.
Show resolved Hide resolved

@classmethod
def flush(cls):
"""Flush the log."""
_get_backend().flush()

@classmethod
def can_log_message(cls, level: int) -> bool:
"""Get whether a message at the given level can be logged."""
koubaa marked this conversation as resolved.
Show resolved Hide resolved
return _get_backend().can_log_message(level)

@classmethod
def debug(cls, msg: str):
"""Write a debug message to the log."""
_get_backend().log_message(logging.DEBUG, LOGGING_CONTEXT, msg)

@classmethod
def error(cls, msg: str):
"""Write a error message to the log."""
_get_backend().log_message(logging.ERROR, LOGGING_CONTEXT, msg)

@classmethod
def info(cls, msg: str):
"""Write a info message to the log."""
koubaa marked this conversation as resolved.
Show resolved Hide resolved
_get_backend().log_message(logging.INFO, LOGGING_CONTEXT, msg)

@classmethod
def warning(cls, msg: str):
"""Write a warning message to the log."""
_get_backend().log_message(logging.WARNING, LOGGING_CONTEXT, msg)

@classmethod
def fatal(cls, msg: str):
"""Write a fatal message to the log."""
_get_backend().log_message(logging.FATAL, LOGGING_CONTEXT, msg)