Skip to content

Commit

Permalink
further steps towards using pycommons
Browse files Browse the repository at this point in the history
  • Loading branch information
thomasWeise committed Mar 11, 2024
1 parent ff802d0 commit 3046c05
Show file tree
Hide file tree
Showing 13 changed files with 95 additions and 190 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
# execute the actual make build process
- name: execute the make build
run: make
# remove mopitpy
# remove moptipy
- name: purge local moptipy installation
run: |
pip uninstall -y moptipy
Expand Down
51 changes: 17 additions & 34 deletions moptipy/api/_process_base.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
"""An internal module with the base class for implementing Processes."""
import os
from io import StringIO
from math import inf, isfinite
from threading import Lock, Timer
Expand Down Expand Up @@ -83,37 +82,26 @@ def _error_1(logger: Logger, title: str, exception_type,
"""
if exception_type or exception_value or traceback:
with logger.text(title=title) as ts:
wt: Final[Callable[[str], None]] = ts.write
if exception_type:
ts.write(KEY_EXCEPTION_TYPE)
ts.write(": ")
if isinstance(exception_type, str):
if exception_type.startswith("<class '"):
exception_type = exception_type[8:-2]
ts.write(exception_type.strip())
else:
ts.write(type_name_of(exception_type))
ts.write(os.linesep)
exception_type = type_name_of(exception_type)
wt(f"{KEY_EXCEPTION_TYPE}: {str.strip(exception_type)}")
if exception_value:
ts.write(KEY_EXCEPTION_VALUE)
ts.write(": ")
ts.write(str(exception_value).strip())
ts.write(os.linesep)
exception_value = str.strip(str(exception_value))
wt(f"{KEY_EXCEPTION_VALUE}: {exception_value}")
if traceback:
ts.write(KEY_EXCEPTION_STACK_TRACE)
ts.write(":")
ts.write(os.linesep)
wt(f"{KEY_EXCEPTION_STACK_TRACE}:")
sio = StringIO()
print_tb(traceback, file=sio)
needs_lb: bool = False
for line in sio.getvalue().strip().split("\n"):
ll: str = line.strip()
if len(ll) <= 0:
for line in str.splitlines(str.strip(sio.getvalue())):
ll: str = str.strip(line)
if str.__len__(ll) <= 0:
continue
ll = ll.replace(ERROR_SECTION_PREFIX, error_repl)
if needs_lb:
ts.write(os.linesep)
needs_lb = True
ts.write(ll)
wt(ll.replace(ERROR_SECTION_PREFIX, error_repl))


def _error_2(logger: Logger, title: str, exception: Exception) -> None:
Expand All @@ -125,29 +113,24 @@ def _error_2(logger: Logger, title: str, exception: Exception) -> None:
created
:param exception: the exception
>>> from moptipy.utils.logger import InMemoryLogger
>>> ime = InMemoryLogger()
>>> from moptipy.utils.logger import PrintLogger
>>> ime = PrintLogger()
>>> def k():
... 1 / 0
>>> try:
... k()
... except Exception as be:
... _error_2(ime, "ERROR", be)
>>> the_log = ime.get_log()
>>> print(the_log[0])
BEGIN_ERROR
>>> print(the_log[1])
exceptionType: ZeroDivisionError
>>> print(the_log[2])
exceptionValue: division by zero
>>> print(the_log[3])
exceptionStackTrace:
>>> print(the_log[-1])
File "<doctest moptipy.api._process_base._error_2[3]>", line 2, in \
<module>
k()
File "<doctest moptipy.api._process_base._error_2[2]>", line 2, in k
1 / 0
END_ERROR
>>> all(ss == ss.strip() for ss in the_log)
True
>>> all(len(ss) > 0 for ss in the_log)
True
"""
_error_1(logger, title, exception_type=exception,
exception_value=str(exception),
Expand Down
4 changes: 2 additions & 2 deletions moptipy/evaluation/end_results.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
PerRunData,
)
from moptipy.evaluation.log_parser import SetupAndStateParser
from moptipy.utils.help import argparser
from moptipy.utils.help import moptipy_argparser
from moptipy.utils.logger import CSV_SEPARATOR
from moptipy.utils.math import try_float_div, try_int
from moptipy.utils.strings import (
Expand Down Expand Up @@ -913,7 +913,7 @@ def lines(self, lines: list[str]) -> bool:

# Run log files to end results if executed as script
if __name__ == "__main__":
parser: Final[argparse.ArgumentParser] = argparser(
parser: Final[argparse.ArgumentParser] = moptipy_argparser(
__file__,
"Convert log files obtained with moptipy to the end results CSV "
"format that can be post-processed or exported to other tools.",
Expand Down
4 changes: 2 additions & 2 deletions moptipy/evaluation/end_statistics.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
KEY_STDDEV,
Statistics,
)
from moptipy.utils.help import argparser
from moptipy.utils.help import moptipy_argparser
from moptipy.utils.logger import CSV_SEPARATOR, SCOPE_SEPARATOR
from moptipy.utils.math import try_int, try_int_div
from moptipy.utils.strings import sanitize_name
Expand Down Expand Up @@ -1276,7 +1276,7 @@ def __inner_sd(s: EndStatistics, ll1=l1) -> int | float | None:

# Run end-results to stat file if executed as script
if __name__ == "__main__":
parser: Final[argparse.ArgumentParser] = argparser(
parser: Final[argparse.ArgumentParser] = moptipy_argparser(
__file__, "Build an end-results statistics CSV file.",
"This program creates a CSV file with basic statistics on the "
"end-of-run state of experiments conducted with moptipy. It "
Expand Down
4 changes: 2 additions & 2 deletions moptipy/evaluation/frequency.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
PerRunData,
)
from moptipy.evaluation.log_parser import SetupAndStateParser
from moptipy.utils.help import argparser
from moptipy.utils.help import moptipy_argparser
from moptipy.utils.logger import CSV_SEPARATOR, SCOPE_SEPARATOR

#: the lower bound of the objective function
Expand Down Expand Up @@ -469,7 +469,7 @@ def lines(self, lines: list[str]) -> bool:

# Print a CSV file
if __name__ == "__main__":
parser: Final[argparse.ArgumentParser] = argparser(
parser: Final[argparse.ArgumentParser] = moptipy_argparser(
__file__, "Collecting the Number of Existing Objective Values.",
"Gather all the existing objective values and store them in a "
"CSV-formatted file.")
Expand Down
4 changes: 2 additions & 2 deletions moptipy/evaluation/ioh_analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@

from moptipy.evaluation.base import F_NAME_RAW, TIME_UNIT_FES, check_f_name
from moptipy.evaluation.progress import Progress
from moptipy.utils.help import argparser
from moptipy.utils.help import moptipy_argparser


def __prefix(s: str) -> str:
Expand Down Expand Up @@ -241,7 +241,7 @@ def __consume(progress: Progress) -> None:

# Run conversion if executed as script
if __name__ == "__main__":
parser: Final[argparse.ArgumentParser] = argparser(
parser: Final[argparse.ArgumentParser] = moptipy_argparser(
__file__,
"Convert experimental results from the moptipy to the "
"IOHanalyzer format.",
Expand Down
2 changes: 1 addition & 1 deletion moptipy/examples/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
You can find many more applications and examples at in our `moptipyapps`
package building on `moptipy` at <https://thomasweise.github.io/moptipyapps>.
Here, we mainly include examples that are useful for verifying the algorithms
and modules implemented in `mopitpy` and those useful for our "Optimization
and modules implemented in `moptipy` and those useful for our "Optimization
Algorithms" book (see <https://thomasweise.github.io/oa>).
"""
4 changes: 2 additions & 2 deletions moptipy/examples/jssp/evaluation.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
plot_stat_gantt_charts,
)
from moptipy.spaces.permutations import Permutations
from moptipy.utils.help import argparser
from moptipy.utils.help import moptipy_argparser
from moptipy.utils.lang import EN
from moptipy.utils.logger import sanitize_name
from moptipy.utils.strings import (
Expand Down Expand Up @@ -594,7 +594,7 @@ def evaluate_experiment(results_dir: str = pp.join(".", "results"),

# Evaluate experiment if run as script
if __name__ == "__main__":
parser: Final[argparse.ArgumentParser] = argparser(
parser: Final[argparse.ArgumentParser] = moptipy_argparser(
__file__, "Evaluate the results of the JSSP example experiment",
"This experiment evaluates all the results of the JSSP example"
" experiment and creates the figures and tables of the "
Expand Down
4 changes: 2 additions & 2 deletions moptipy/examples/jssp/experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from moptipy.operators.permutations.op1_swap2 import Op1Swap2
from moptipy.operators.permutations.op1_swapn import Op1SwapN
from moptipy.spaces.permutations import Permutations
from moptipy.utils.help import argparser
from moptipy.utils.help import moptipy_argparser

#: The default instances to be used in our experiment. These have been
#: computed via instance_selector.propose_instances.
Expand Down Expand Up @@ -173,7 +173,7 @@ def creator(inst: Instance, algor: Callable = algo) -> Execution:

# Execute experiment if run as script
if __name__ == "__main__":
parser: Final[argparse.ArgumentParser] = argparser(
parser: Final[argparse.ArgumentParser] = moptipy_argparser(
__file__, "Execute the JSSP example experiment.",
"Execute an example experiment on the Job "
"Shop Scheduling Problem (JSSP).")
Expand Down
6 changes: 3 additions & 3 deletions moptipy/examples/jssp/instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
deServlet/dbt_derivate_00001373/Dissertation.pdf
"""
from importlib import resources # nosem
from typing import Final, cast
from typing import Final, Iterable, cast

import numpy as np
from pycommons.types import check_int_range, type_error
Expand Down Expand Up @@ -416,7 +416,7 @@ def from_text(name: str, rows: list[str]) -> "Instance":
makespan_lower_bound=makespan_lower_bound)

@staticmethod
def from_stream(name: str, stream) -> "Instance":
def from_stream(name: str, stream: Iterable[str]) -> "Instance":
"""
Load an instance from a text stream.
Expand All @@ -427,7 +427,7 @@ def from_stream(name: str, stream) -> "Instance":
state = 0
rows: list[str] | None = None
for linestr in stream:
line = str(linestr).strip()
line = str.strip(linestr)
if len(line) <= 0:
continue
if state == 0:
Expand Down
103 changes: 12 additions & 91 deletions moptipy/utils/help.py
Original file line number Diff line number Diff line change
@@ -1,84 +1,15 @@
"""Print a help screen."""


import argparse
import os.path
import sys
from typing import Final

from pycommons.io.path import Path
from pycommons.types import type_error
from pycommons.io.arguments import make_argparser, make_epilog

from moptipy.version import __version__

#: The default argument parser for moptipy executables.
__DEFAULT_ARGUMENTS: Final[argparse.ArgumentParser] = argparse.ArgumentParser(
epilog="Copyright\u00a0\u00a9\u00a02022\u00a0Thomas\u00a0WEISE, "
"GNU\u00a0GENERAL\u00a0PUBLIC\u00a0LICENSE\u00a0Version\u00a03,"
"\u00a029\u00a0June\u00a02007, "
"https://thomasweise.github.io/moptipy, "
"[email protected],\u00a0[email protected]",
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
__DEFAULT_ARGUMENTS.add_argument(
"--version", action="version", version=__version__)


def __get_python_interpreter_short() -> str:
"""
Get the python interpreter.
:returns: the fully-qualified path
"""
inter: Final[str] = Path(sys.executable)
bn = os.path.basename(inter)
if bn.startswith("python3."):
bn2 = bn[:7]
interp2 = os.path.join(os.path.dirname(inter), bn2)
if os.path.exists(interp2) and os.path.isfile(interp2) \
and (Path(interp2) == inter):
return bn2
return bn


#: The python interpreter in short form.
__INTERPRETER_SHORT: Final[str] = __get_python_interpreter_short()
del __get_python_interpreter_short

#: the base path of the moptipy package
__BASE_PATH: Final[str] = Path(os.path.dirname(
os.path.dirname(os.path.dirname(Path(__file__))))) + os.sep


def __get_prog(file: str) -> str:
"""
Get the program as to be displayed by the help screen.
The result of this function applied to the `__file__` special
variable should be put into the `prog` argument of the constructor
of :class:`argparse.ArgumentParser`.
:param file: the calling python script
:return: the program string
"""
if not isinstance(file, str):
raise type_error(file, "file", str)

# get the module minus the base path and extension
module: str = Path(file)
end: int = len(module)
start: int = 0
if module.endswith(".py"):
end -= 3
if module.startswith(__BASE_PATH):
start += len(__BASE_PATH)
module = module[start:end].replace(os.sep, ".")

return f"{__INTERPRETER_SHORT} -m {module}"


def argparser(file: str, description: str,
epilog: str) -> argparse.ArgumentParser:
def moptipy_argparser(file: str, description: str,
epilog: str) -> argparse.ArgumentParser:
"""
Create an argument parser with default settings.
Expand All @@ -87,26 +18,16 @@ def argparser(file: str, description: str,
:param epilog: the epilogue string
:returns: the argument parser
>>> ap = argparser(__file__, "This is a test program.", "This is a test.")
>>> ap = moptipy_argparser(
... __file__, "This is a test program.", "This is a test.")
>>> isinstance(ap, argparse.ArgumentParser)
True
>>> "Copyright" in ap.epilog
True
"""
if not isinstance(file, str):
raise type_error(file, "file", str)
if len(file) <= 3:
raise ValueError(f"invalid file={file!r}.")
if not isinstance(description, str):
raise type_error(description, "description", str)
if len(description) <= 12:
raise ValueError(f"invalid description={description!r}.")
if not isinstance(epilog, str):
raise type_error(epilog, "epilog", str)
if len(epilog) <= 10:
raise ValueError(f"invalid epilog={epilog!r}.")
return argparse.ArgumentParser(
parents=[__DEFAULT_ARGUMENTS], prog=__get_prog(file),
description=description.strip(),
epilog=f"{epilog.strip()} {__DEFAULT_ARGUMENTS.epilog}",
formatter_class=__DEFAULT_ARGUMENTS.formatter_class)
return make_argparser(
file, description,
make_epilog(epilog, 2022, 2024, "Thomas Weise",
url="https://thomasweise.github.io/moptipy",
email="[email protected], [email protected]"),
__version__)
Loading

0 comments on commit 3046c05

Please sign in to comment.