Skip to content

Commit

Permalink
Move Trace classes to new file
Browse files Browse the repository at this point in the history
  • Loading branch information
jmgate committed Nov 9, 2021
1 parent 2b781a4 commit fbe9919
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 159 deletions.
20 changes: 20 additions & 0 deletions doc/source/Trace.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
Trace Class Reference
=====================

.. autoclass:: shelllogger.Trace.Trace
:noindex:

.. automethod:: shelllogger.Trace::trace_collector
:noindex:

STrace
------

.. autoclass:: shelllogger.Trace.STrace
:noindex:

LTrace
------

.. autoclass:: shelllogger.Trace.LTrace
:noindex:
21 changes: 0 additions & 21 deletions doc/source/classes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,6 @@ JSON Serialization
.. autoclass:: shelllogger.ShellLoggerEncoder
:noindex:

Trace
-----

.. autoclass:: shelllogger.classes.Trace
:noindex:

.. automethod:: shelllogger.classes::trace_collector
:noindex:

Strace
~~~~~~

.. autoclass:: shelllogger.ShellLogger.Strace
:noindex:

Ltrace
~~~~~~

.. autoclass:: shelllogger.ShellLogger.Ltrace
:noindex:

AbstractMethod
--------------
.. autoexception:: shelllogger.classes.AbstractMethod
Expand Down
1 change: 1 addition & 0 deletions doc/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ shelllogger
ShellLogger
Shell
StatsCollector
Trace
classes
util
todo
Expand Down
60 changes: 1 addition & 59 deletions src/shelllogger/ShellLogger.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#!/usr/bin/env python3

from __future__ import annotations
from .classes import (Trace, trace_collector)
from .Shell import Shell
from .StatsCollector import stats_collectors
from .Trace import trace_collector
from .util import (nested_simplenamespace_to_dict, opening_html_text,
closing_html_text, append_html, html_message_card,
message_card, command_card, child_logger_card,
Expand Down Expand Up @@ -644,64 +644,6 @@ def auxiliary_information(self) -> SimpleNamespace:
ulimit=ulimit)


@Trace.subclass
class Strace(Trace):
"""
An interface between :class:`ShellLogger` and the ``strace``
command.
"""
trace_name = "strace"

def __init__(self, **kwargs) -> None:
"""
Initialize the :class:`Strace` instance.
"""
super().__init__(**kwargs)
self.summary = True if kwargs.get("summary") else False
self.expression = kwargs.get("expression")

@property
def trace_args(self) -> str:
"""
Wraps a command in a ``strace`` command.
"""
args = f"strace -f -o {self.output_path}"
if self.summary:
args += " -c"
if self.expression:
args += f" -e '{self.expression}'"
return args


@Trace.subclass
class Ltrace(Trace):
"""
An interface between :class:`ShellLogger` and the ``ltrace``
command.
"""
trace_name = "ltrace"

def __init__(self, **kwargs):
"""
Initialize the :class:`Ltrace` instance.
"""
super().__init__(**kwargs)
self.summary = True if kwargs.get("summary") else False
self.expression = kwargs.get("expression")

@property
def trace_args(self):
"""
Wraps a command in a ``ltrace`` command.
"""
args = f"ltrace -C -f -o {self.output_path}"
if self.summary:
args += " -c"
if self.expression:
args += f" -e '{self.expression}'"
return args


class ShellLoggerEncoder(json.JSONEncoder):
"""
This is a helper class to make the :class:`ShellLogger` class JSON
Expand Down
140 changes: 140 additions & 0 deletions src/shelllogger/Trace.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
#!/usr/bin/env python3

from __future__ import annotations
from .classes import AbstractMethod
from abc import abstractmethod
from pathlib import Path


def trace_collector(**kwargs) -> Trace:
"""
A factory method that returns any subclass of :class:`Trace` that
has the ``@Trace.subclass`` decorator applied to it.
Parameters:
**kwargs: Any supported arguments of the :class:`Trace`
subclass.
Returns:
A single instance of a :class:`Trace` subclass.
"""
trace_name = kwargs["trace"]
collectors = [c for c in Trace.subclasses if c.trace_name == trace_name]
if len(collectors) == 1:
collector = collectors[0]
return collector(**kwargs)
elif len(collectors) == 0:
raise RuntimeError(f"Unsupported trace type: {trace_name}")
else:
raise RuntimeError(f"Multiple trace types match '{trace_name}'.")


class Trace:
"""
Provides an interface for the :class:`ShellLogger` to run commands
with a certain trace (e.g., ``strace`` or ``ltrace``).
"""
trace_name = "undefined" # Should be defined by subclasses.
subclasses = []

@staticmethod
def subclass(trace_subclass: type):
"""
This is a class decorator that adds to a list of supported
:class:`Trace` classes for the :func:`trace_collector` factory
method.
"""
if issubclass(trace_subclass, Trace):
Trace.subclasses.append(trace_subclass)
return trace_subclass

def __init__(self, **kwargs):
"""
Initialize the :class:`Trace` object, setting up the output file
where the trace information will be written.
"""
if kwargs.get("trace_path"):
self.output_path = Path(kwargs["trace_path"])
else:
self.output_path = Path(f"{self.trace_name}.log")

@property
@abstractmethod
def trace_args(self) -> None:
"""
The trace command and the arguments you pass to it, but not the
command you're tracing. E.g., return `strace -f -c -e "open"`.
Raises:
AbstractMethod: This needs to be overridden by subclasses.
"""
raise AbstractMethod()

def command(self, command: str):
"""
Return a command that runs a trace on ``command``. E.g., ``ls
-l`` might get translated to ``strace -f -c -e 'open' -- ls
-l``.
Parameters:
command: The command to be traced.
"""
return f"{self.trace_args} -- {command}"


@Trace.subclass
class STrace(Trace):
"""
An interface between :class:`ShellLogger` and the ``strace``
command.
"""
trace_name = "strace"

def __init__(self, **kwargs) -> None:
"""
Initialize the :class:`STrace` instance.
"""
super().__init__(**kwargs)
self.summary = True if kwargs.get("summary") else False
self.expression = kwargs.get("expression")

@property
def trace_args(self) -> str:
"""
Wraps a command in a ``strace`` command.
"""
args = f"strace -f -o {self.output_path}"
if self.summary:
args += " -c"
if self.expression:
args += f" -e '{self.expression}'"
return args


@Trace.subclass
class LTrace(Trace):
"""
An interface between :class:`ShellLogger` and the ``ltrace``
command.
"""
trace_name = "ltrace"

def __init__(self, **kwargs):
"""
Initialize the :class:`LTrace` instance.
"""
super().__init__(**kwargs)
self.summary = True if kwargs.get("summary") else False
self.expression = kwargs.get("expression")

@property
def trace_args(self):
"""
Wraps a command in a ``ltrace`` command.
"""
args = f"ltrace -C -f -o {self.output_path}"
if self.summary:
args += " -c"
if self.expression:
args += f" -e '{self.expression}'"
return args
79 changes: 0 additions & 79 deletions src/shelllogger/classes.py
Original file line number Diff line number Diff line change
@@ -1,85 +1,6 @@
#!/usr/bin/env python3

from __future__ import annotations
from abc import abstractmethod
import inspect
from pathlib import Path


def trace_collector(**kwargs) -> Trace:
"""
A factory method that returns any subclass of :class:`Trace` that
has the ``@Trace.subclass`` decorator applied to it.
Parameters:
**kwargs: Any supported arguments of the :class:`Trace`
subclass.
Returns:
A single instance of a :class:`Trace` subclass.
"""
trace_name = kwargs["trace"]
collectors = [c for c in Trace.subclasses if c.trace_name == trace_name]
if len(collectors) == 1:
collector = collectors[0]
return collector(**kwargs)
elif len(collectors) == 0:
raise RuntimeError(f"Unsupported trace type: {trace_name}")
else:
raise RuntimeError(f"Multiple trace types match '{trace_name}'.")


class Trace:
"""
Provides an interface for the :class:`ShellLogger` to run commands
with a certain trace (e.g., ``strace`` or ``ltrace``).
"""
trace_name = "undefined" # Should be defined by subclasses.
subclasses = []

@staticmethod
def subclass(trace_subclass: type):
"""
This is a class decorator that adds to a list of supported
:class:`Trace` classes for the :func:`trace_collector` factory
method.
"""
if issubclass(trace_subclass, Trace):
Trace.subclasses.append(trace_subclass)
return trace_subclass

def __init__(self, **kwargs):
"""
Initialize the :class:`Trace` object, setting up the output file
where the trace information will be written.
"""
if kwargs.get("trace_path"):
self.output_path = Path(kwargs["trace_path"])
else:
self.output_path = Path(f"{self.trace_name}.log")

@property
@abstractmethod
def trace_args(self) -> None:
"""
The trace command and the arguments you pass to it, but not the
command you're tracing. E.g., return `strace -f -c -e "open"`.
Raises:
AbstractMethod: This needs to be overridden by subclasses.
"""
raise AbstractMethod()

def command(self, command: str):
"""
Return a command that runs a trace on ``command``. E.g., ``ls
-l`` might get translated to ``strace -f -c -e 'open' -- ls
-l``.
Parameters:
command: The command to be traced.
"""
return f"{self.trace_args} -- {command}"


class AbstractMethod(NotImplementedError):
Expand Down

0 comments on commit fbe9919

Please sign in to comment.