Skip to content

Commit

Permalink
initial support for inheritence (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
UnquietCode committed Aug 12, 2019
1 parent d2f7a13 commit df69032
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 9 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
**/__pycache__/*
*.log
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,22 @@ if __name__ == '__main__':
CustomRunbook.main()
```

The run-book object can also be instantiated and run directly, like so:
The run-book object can also be instantiated and run directly.

```python
book = CustomRunbook(file_path="path/to/file")
book.run()
```

You should avoid using the step names `run` and `main`, which are already defined.
**You should avoid using the step names `run` and `main`**, which are already defined. If you need to override these
methods to define custom behavior that is fine.

As steps are completed, the results are written out to a log file. You can set a custom log file path by passing an argument to main, as in:
As steps are completed, the results are written out to a log file. You can set a custom log file path by passing
an argument to main, as in:

```
python3 my_runbook.py output.log
```

When using the same log file, already completed steps will be skipped. Any new steps found in the `Runbook` class and not in the log will be processed.
When reusing the same log file, already completed steps will be skipped. Any new steps found in the `Runbook`
and not already in the log will be processed as normal, with results appended to the end of the log file.
11 changes: 11 additions & 0 deletions class_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from class_test_books import BookA, BookB


class BookC(BookB, BookA):

def step_c4():
pass


if __name__ == '__main__':
BookC.main()
18 changes: 18 additions & 0 deletions class_test_books.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from runbook import Runbook



class BookA(Runbook):

def step_b1():
pass


class BookB(Runbook):

def step_a2(self):
pass

@staticmethod
def step_a3():
pass
54 changes: 49 additions & 5 deletions runbook.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,13 +133,50 @@ def _wait_for_response(self):
print("\n\tinvalid response\n")



def _get_steps(self) -> List[Step]:

# list class hierarchy, in order
classes = list(inspect.getmro(type(self)))
classes.reverse()

# list methods by class, in order
methods_by_class = {}
all_methods_by_class = {}

# get all methods in the class
methods = inspect.getmembers(self, predicate=inspect.ismethod)
for c in classes:
methods_by_class[c] = inspect.getmembers(c, lambda _:inspect.ismethod(_) or inspect.isfunction(_))
all_methods_by_class[c] = []

# sort methods by declaration order
methods = sorted(methods, key=lambda _: _[1].__func__.__code__.co_firstlineno)
def key_filter(value):
value = value[1]

if hasattr(value, '__func__'):
return value.__func__.__code__.co_firstlineno
else:
return value.__code__.co_firstlineno

all_methods = inspect.getmembers(self, lambda _:inspect.ismethod(_) or inspect.isfunction(_))
all_methods = sorted(all_methods, key=key_filter)

for n1, m1 in all_methods:
for clazz, class_methods in methods_by_class.items():
should_continue = True

for n2, m2 in class_methods:
if n1 == n2:
all_methods_by_class[clazz].append((n1,m1))
should_continue = False
break

if should_continue is False:
break

methods = []

for a, b in all_methods_by_class.items():
methods.extend(b)

# build up a list of steps
steps:List[Step] = []
Expand All @@ -157,7 +194,8 @@ def _get_steps(self) -> List[Step]:

# if method is zero arg, call the unbound class method
# (as a convenience for @staticmethod)
function_signature = inspect.signature(method.__func__)
function = method.__func__ if hasattr(method, '__func__') else method
function_signature = inspect.signature(function)

if len(function_signature.parameters) == 0:
method = getattr(type(self), method_name)
Expand Down Expand Up @@ -210,6 +248,8 @@ def _read_file(file_path):


def _write_result(self, step:Step, result, negative=False, reason=None):

# open a file in unicode append mode
with open(self.file_path, "a+") as file:

# write name header
Expand All @@ -229,7 +269,11 @@ def _write_result(self, step:Step, result, negative=False, reason=None):
file.write("\n")

# write generic response line
file.write(f"responded `{result}` at {datetime.now().strftime('%H:%M:%S')} on {datetime.now().strftime('%d/%m/%Y')}\n")
file.write(
f"responded `{result}` "
f"at {datetime.now().strftime('%H:%M:%S')} "
f"on {datetime.now().strftime('%d/%m/%Y')}\n"
)

# write negative response line
if negative is True and reason:
Expand Down

0 comments on commit df69032

Please sign in to comment.