summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbd <bdunahu@operationnull.com>2025-11-30 23:22:01 -0500
committerbd <bdunahu@operationnull.com>2025-11-30 23:22:01 -0500
commit8041035d3ce04e72ab0dadef325c03447dc6d87f (patch)
treee23bc1be9b268ff672f17e76229f113a49423db3
parent444554a579fe57e3bd470821d77c3cf4af60c56f (diff)
nemesis: split nemesis.py into utils.py, main.py.
nemesis/nemesis.py: remove main function, parsing, utility files [new file]: nemesis/utils.py [new file]: main.py Update copyright information
-rw-r--r--main.py99
-rw-r--r--nemesis/causal_event_loop.py5
-rw-r--r--nemesis/html_gen.py20
-rwxr-xr-xnemesis/nemesis.py118
-rw-r--r--nemesis/utils.py34
5 files changed, 164 insertions, 112 deletions
diff --git a/main.py b/main.py
new file mode 100644
index 0000000..e468379
--- /dev/null
+++ b/main.py
@@ -0,0 +1,99 @@
+#!/usr/bin/env python3
+'''
+ _/ _/ _/
+ _/_/ _/ _/_/ _/_/_/ _/_/ _/_/ _/_/_/ _/_/_/
+ _/ _/ _/ _/_/_/_/ _/ _/ _/ _/_/_/_/ _/_/ _/ _/_/
+ _/ _/_/ _/ _/ _/ _/ _/ _/_/ _/ _/_/
+ _/ _/ _/_/_/ _/ _/ _/ _/_/_/ _/_/_/ _/ _/_/_/
+
+Copyright:
+
+ Copyright © 2025 bdunahu <bdunahu@operationnull.com>
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+Commentary:
+Code:
+'''
+import argparse
+import sys
+import traceback
+from nemesis.nemesis import Nemesis
+from nemesis.utils import validate_html, validate_dir
+
+def parse_args():
+ parser = argparse.ArgumentParser(
+ usage='%(prog)s [args] -- prog'
+ )
+
+ parser.add_argument('-i', '--interval',
+ help='The minimum amount of time inbetween \
+ samples in seconds.',
+ metavar='',
+ type=float,
+ default=0.01)
+ parser.add_argument('-e', '--experiment-duration',
+ help='The performance experiment duration. Defaults to 3 seconds.',
+ metavar='',
+ type=float,
+ default=3)
+ parser.add_argument('-f', '--filename',
+ help='The filename to write results to. Must name an HTML file.',
+ metavar='',
+ type=validate_html,
+ default="results.html")
+ parser.add_argument('--include-paths',
+ help='Specify the path(s) containing files to profile. If a file is in this path, it is a candidate for optimization.',
+ nargs="+",
+ type=validate_dir,
+ required=True)
+ parser.add_argument('--exclude-paths',
+ help='Specify the path(s) containing files to exclude profile. Takes priority over --include-paths.',
+ nargs="*",
+ type=validate_dir,
+ required=False,
+ default=[])
+ parser.add_argument('prog',
+ type=str,
+ nargs='*',
+ help='Path to the python script and its arguments.')
+ return parser.parse_args()
+
+the_globals = {
+ '__name__': '__main__',
+ '__doc__': None,
+ '__package__': None,
+ '__loader__': globals()['__loader__'],
+ '__spec__': None,
+ '__annotations__': {},
+ '__builtins__': globals()['__builtins__'],
+ '__file__': None,
+ '__cached__': None,
+}
+
+if __name__ == "__main__":
+ args = parse_args()
+ sys.argv = args.prog
+ try:
+ with open(args.prog[0], 'r', encoding='utf-8') as fp:
+ code = compile(fp.read(), args.prog[0], "exec")
+ Nemesis(args.experiment_duration,
+ args.include_paths,
+ args.exclude_paths,
+ args.filename,
+ args.prog[0],
+ args.interval).start()
+ exec(code, the_globals)
+ Nemesis.stop()
+ except Exception:
+ traceback.print_exc()
diff --git a/nemesis/causal_event_loop.py b/nemesis/causal_event_loop.py
index 9b774d0..f9cffb4 100644
--- a/nemesis/causal_event_loop.py
+++ b/nemesis/causal_event_loop.py
@@ -1,5 +1,7 @@
'''
-Copyright 2025 bdunahu
+Copyright:
+
+ Copyright © 2025 bdunahu <bdunahu@operationnull.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -14,7 +16,6 @@ Copyright 2025 bdunahu
limitations under the License.
Commentary:
-
Code:
'''
from typing import TYPE_CHECKING, Any, Callable, Self
diff --git a/nemesis/html_gen.py b/nemesis/html_gen.py
index 85d2cae..2a417fc 100644
--- a/nemesis/html_gen.py
+++ b/nemesis/html_gen.py
@@ -1,3 +1,23 @@
+'''
+Copyright:
+
+ Copyright © 2025 bdunahu <bdunahu@operationnull.com>
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+Commentary:
+Code:
+'''
from typing import TYPE_CHECKING
if TYPE_CHECKING:
import collections
diff --git a/nemesis/nemesis.py b/nemesis/nemesis.py
index 9d4014c..812cd2d 100755
--- a/nemesis/nemesis.py
+++ b/nemesis/nemesis.py
@@ -1,13 +1,7 @@
-#!/usr/bin/env python3
'''
- _/ _/ _/
- _/_/ _/ _/_/ _/_/_/ _/_/ _/_/ _/_/_/ _/_/_/
- _/ _/ _/ _/_/_/_/ _/ _/ _/ _/_/_/_/ _/_/ _/ _/_/
- _/ _/_/ _/ _/ _/ _/ _/ _/_/ _/ _/_/
- _/ _/ _/_/_/ _/ _/ _/ _/_/_/ _/_/_/ _/ _/_/_/
+Copyright:
-
-Copyright 2025 bdunahu
+ Copyright © 2025 bdunahu <bdunahu@operationnull.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -24,22 +18,20 @@ Copyright 2025 bdunahu
Commentary:
Code:
'''
-from typing import Any, Self
-from causal_event_loop import CausalEventLoop
+from nemesis.causal_event_loop import CausalEventLoop
from collections import defaultdict
-from html_gen import plot_results
-import argparse
+from nemesis.html_gen import plot_results
+from pathlib import Path
+from typing import Any, Self
import asyncio
-import os
import inspect
+import os
import random
import signal
import sys
import threading
import time
-import traceback
import types
-from pathlib import Path
CO_COROUTINE = inspect.CO_COROUTINE
@@ -114,21 +106,6 @@ class Nemesis(object):
Nemesis.signal_interval)
@staticmethod
- def print_results():
- for coro_name, x_values in Nemesis.results.items():
- print(f'Results for {coro_name:}')
- for speedup, experiments in x_values.items():
- print(f' {speedup * 100}% speedup:')
- for experiment in experiments:
- num_callbacks = len(experiment)
- if num_callbacks > 0:
- total_wait = sum([cb[1] for cb in experiment])
- latency = total_wait / num_callbacks
- print(f' latency: {latency}')
- print(f' callbacks processed: {num_callbacks}')
- print(f'')
-
- @staticmethod
def stop() -> None:
signal.setitimer(signal.ITIMER_REAL, 0)
plot_results(Nemesis.results, Nemesis.filename, Nemesis.prog)
@@ -144,7 +121,7 @@ class Nemesis(object):
loops = Nemesis._get_event_loops()
for loop in loops:
if not isinstance(loop, CausalEventLoop):
- raise RuntimeError(f"Nemesis requires a custom event loop to insert slowdowns. You must start the event loop with `asyncio.run(your_coro(), loop_factory=causal_loop_factory)'.")
+ raise RuntimeError(f"Nemesis requires a custom event loop to insert slowdowns. You must start the event loop with `asyncio.run(your_coro(), loop_factory=causal_loop_factory)'. Received: {type(loop)}")
loop.set_speedup(speedup)
Nemesis.experiment_data = Experiment(loops)
@@ -313,82 +290,3 @@ class Nemesis(object):
# Stop masking Cython bugs, expose them in a friendly way.
coro_name = f'<{type(coro).__name__} without __name__>'
return f'{coro_name}()'
-
-the_globals = {
- '__name__': '__main__',
- '__doc__': None,
- '__package__': None,
- '__loader__': globals()['__loader__'],
- '__spec__': None,
- '__annotations__': {},
- '__builtins__': globals()['__builtins__'],
- '__file__': None,
- '__cached__': None,
-}
-
-def validate_dir(path_str: str) -> Path:
- p = Path(path_str).expanduser().resolve()
- if not p.exists():
- raise ValueError(f"Profile path does not exist: {p}")
- if not p.is_dir():
- raise ValueError(f"Can't profile a non-dir: {p}")
- return p
-
-def validate_html(filename: str) -> str:
- if not filename.lower().endswith('.html'):
- raise argparse.ArgumentTypeError(f"Did not name an HTML file: '{filename}'")
- return filename
-
-if __name__ == "__main__":
- # parses CLI arguments and facilitates profiler runtime.
- parser = argparse.ArgumentParser(
- usage='%(prog)s [args] -- prog'
- )
-
- parser.add_argument('-i', '--interval',
- help='The minimum amount of time inbetween \
- samples in seconds.',
- metavar='',
- type=float,
- default=0.01)
- parser.add_argument('-e', '--experiment-duration',
- help='The performance experiment duration. Defaults to 3 seconds.',
- metavar='',
- type=float,
- default=3)
- parser.add_argument('-f', '--filename',
- help='The filename to write results to. Must name an HTML file.',
- metavar='',
- type=validate_html,
- default="results.html")
- parser.add_argument('--include-paths',
- help='Specify the path(s) containing files to profile. If a file is in this path, it is a candidate for optimization.',
- nargs="+",
- type=validate_dir,
- required=True)
- parser.add_argument('--exclude-paths',
- help='Specify the path(s) containing files to exclude profile. Takes priority over --include-paths.',
- nargs="*",
- type=validate_dir,
- required=False,
- default=[])
- parser.add_argument('prog',
- type=str,
- nargs='*',
- help='Path to the python script and its arguments.')
- args = parser.parse_args()
-
- sys.argv = args.prog
- try:
- with open(args.prog[0], 'r', encoding='utf-8') as fp:
- code = compile(fp.read(), args.prog[0], "exec")
- Nemesis(args.experiment_duration,
- args.include_paths,
- args.exclude_paths,
- args.filename,
- args.prog[0],
- args.interval).start()
- exec(code, the_globals)
- Nemesis.stop()
- except Exception:
- traceback.print_exc()
diff --git a/nemesis/utils.py b/nemesis/utils.py
new file mode 100644
index 0000000..0cee784
--- /dev/null
+++ b/nemesis/utils.py
@@ -0,0 +1,34 @@
+'''
+Copyright:
+
+ Copyright © 2025 bdunahu <bdunahu@operationnull.com>
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+Commentary:
+Code:
+'''
+from pathlib import Path
+
+def validate_dir(path_str: str) -> Path:
+ p = Path(path_str).expanduser().resolve()
+ if not p.exists():
+ raise ValueError(f"Profile path does not exist: {p}")
+ if not p.is_dir():
+ raise ValueError(f"Can't profile a non-dir: {p}")
+ return p
+
+def validate_html(filename: str) -> str:
+ if not filename.lower().endswith('.html'):
+ raise argparse.ArgumentTypeError(f"Did not name an HTML file: '{filename}'")
+ return filename