Skip to content
This repository was archived by the owner on Mar 9, 2026. It is now read-only.

Commit cd9f204

Browse files
committed
Implement a basic __main__.py file able to process a --excavator arg.
This commit adds a basic argparse based package level main file which given a filename and a named excavator will perform an excavation and output the usual HTML files. By default this uses the standard temporary path, but additionally support specifying -d and allow a directory argument of "." which will cause outputting output folder in the cwd: ./output/_autoarchaologist In order to ensure things are working include tests of the new command line as well as the basic operation of excavating the example file. The tests check the expected HTML files were written for this known input and assert some basic properties of the excavated artifacts. While here repair the example excavation. The excavation it executes is explicitly declared thus CLI selectable as --excavator examples.showcase
1 parent 3374d44 commit cd9f204

File tree

11 files changed

+281
-26
lines changed

11 files changed

+281
-26
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,10 @@ __pycache__/
66
# Sphinx documentation
77
docs/_build/
88

9+
# Build files
10+
venv/
11+
912
# Temporary files
1013
_.*
14+
/output/
15+
/tests/_scratch/

Makefile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
default: example
2+
3+
example:
4+
./venv/bin/python3 run_example.py -d .
5+
6+
test:
7+
@./venv/bin/python3 -m unittest

autoarchaeologist/__main__.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import argparse
2+
import importlib
3+
import os
4+
import sys
5+
6+
from autoarchaeologist import Excavation
7+
8+
EXCAVATORS = [
9+
'examples.showcase'
10+
]
11+
12+
13+
def action_for_args(args):
14+
if args.excavator is not None:
15+
return ("excavator", load_excavator_by_name(args.excavator))
16+
else:
17+
raise argparse.ArgumentError(None, "no valid excavation was requsted")
18+
19+
20+
def load_excavator_by_name(excavator):
21+
package_name, excavation_prefix = excavator.split('.')
22+
excavation_name = f"{excavation_prefix}_excavation"
23+
24+
exacations_package = importlib.import_module(package_name)
25+
return getattr(exacations_package, excavation_name)
26+
27+
28+
def parse_arguments(argv=None):
29+
parser = argparse.ArgumentParser()
30+
parser.add_argument("-d", "--dir", default="/tmp/_autoarchaologist")
31+
parser.add_argument('--excavator', choices=EXCAVATORS)
32+
parser.add_argument('filename')
33+
34+
return parser.parse_args(args=argv)
35+
36+
37+
def process_arguments(args):
38+
if args.dir == ".":
39+
args.dir = os.path.join(os.getcwd(), "output", "_autoarchaologist")
40+
if args.filename is not None:
41+
args.filename = os.path.abspath(args.filename)
42+
else:
43+
raise ValueError()
44+
45+
return args
46+
47+
48+
def perform_excavation(args):
49+
match action_for_args(args):
50+
case "excavator", AnExcavation:
51+
assert issubclass(AnExcavation, Excavation)
52+
ctx = AnExcavation(html_dir=args.dir)
53+
case action, _:
54+
raise NotImplementedError(f"action: {action}")
55+
56+
ff = ctx.add_file_artifact(args.filename)
57+
58+
ctx.start_examination()
59+
60+
return ctx
61+
62+
63+
if __name__ == "__main__":
64+
args = process_arguments(parse_arguments())
65+
66+
try:
67+
os.mkdir(args.dir)
68+
except FileExistsError:
69+
pass
70+
71+
ctx = perform_excavation(args)
72+
ctx.produce_html()
73+
print("Now point your browser at", ctx.filename_for(ctx).link)

ddhf/ddhf/decorated_context.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,13 +110,29 @@ def from_argv(self):
110110
"AUTOARCHAEOLOGIST_BITSTORE_CACHE": "ddhf_bitstore_cache",
111111
}
112112

113-
def main(job, html_subdir="tmp", **kwargs):
113+
def parse_arguments(argv=None):
114+
parser = argparse.ArgumentParser()
115+
parser.add_argument('-o', '--out', default='/tmp/_autoarchaologist')
116+
117+
args = parser.parse_args(args=argv)
118+
if args.out == '.':
119+
args.out = os.path.join(os.getcwd(), "_autoarchaologist")
120+
return args
121+
122+
def main(job, html_subdir, **kwargs):
123+
args = parse_arguments()
124+
kwargs["html_dir"] = args.out
125+
114126
''' A standard main routine to reduce boiler-plate '''
115127
for key in os.environ:
116128
i = OK_ENVS.get(key)
117129
if i:
118130
kwargs[i] = os.environ[key]
119131

132+
if 'html_dir' not in kwargs:
133+
raise AttributeError("missing: html_dir")
134+
135+
120136
kwargs['html_dir'] = os.path.join(kwargs['html_dir'], html_subdir)
121137
kwargs.setdefault('download_links', True)
122138
kwargs.setdefault('download_limit', 1 << 20)

examples/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from .examples_showcase import ShowcaseExcacation
2+
3+
showcase_excavation = ShowcaseExcacation

examples/examples_showcase.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from autoarchaeologist.base.excavation import Excavation
2+
from autoarchaeologist.generic.bigtext import BigText
3+
from autoarchaeologist.generic.samesame import SameSame
4+
from autoarchaeologist.data_general.absbin import AbsBin
5+
from autoarchaeologist.data_general.papertapechecksum import DGC_PaperTapeCheckSum
6+
7+
8+
class ShowcaseExcacation(Excavation):
9+
def __init__(self, **kwargs):
10+
super().__init__(**kwargs)
11+
12+
self.add_examiner(BigText)
13+
self.add_examiner(AbsBin)
14+
self.add_examiner(DGC_PaperTapeCheckSum)
15+
self.add_examiner(SameSame)

output/.gitkeep

Whitespace-only changes.

run_example.py

Lines changed: 10 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,17 @@
1-
21
import os
2+
import sys
3+
from types import SimpleNamespace
34

4-
import autoarchaeologist
5-
6-
from autoarchaeologist.generic.bigdigits import BigDigits
7-
from autoarchaeologist.generic.samesame import SameSame
8-
from autoarchaeologist.data_general.absbin import AbsBin
9-
from autoarchaeologist.data_general.papertapechecksum import DGC_PaperTapeCheckSum
10-
5+
from autoarchaeologist.__main__ import parse_arguments, process_arguments, perform_excavation
6+
from examples import ShowcaseExcacation
117

128
if __name__ == "__main__":
9+
argv = sys.argv[1:]
10+
# force the example as the filename
11+
argv.append("examples/30001393.bin")
12+
args = process_arguments(parse_arguments(argv=argv))
1313

14-
ctx = autoarchaeologist.Excavation()
15-
16-
ctx.add_examiner(BigDigits)
17-
ctx.add_examiner(AbsBin)
18-
ctx.add_examiner(DGC_PaperTapeCheckSum)
19-
ctx.add_examiner(SameSame)
20-
21-
ff = ctx.add_file_artifact("examples/30001393.bin")
22-
23-
ctx.start_examination()
24-
25-
try:
26-
os.mkdir("/tmp/_autoarchaologist")
27-
except FileExistsError:
28-
pass
29-
30-
ctx.produce_html(html_dir="/tmp/_autoarchaologist")
14+
ctx = perform_excavation(args, ("excavator", ShowcaseExcacation))
15+
ctx.produce_html()
3116

3217
print("Now point your browser at", ctx.filename_for(ctx).link)

tests/__init__.py

Whitespace-only changes.

tests/test_example.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import os
2+
import shutil
3+
from types import SimpleNamespace
4+
import unittest
5+
6+
TESTS_DIR = os.path.dirname(os.path.abspath(__file__))
7+
SCRATCH_DIR = os.path.join(TESTS_DIR, "_scratch")
8+
9+
from autoarchaeologist.__main__ import perform_excavation
10+
11+
12+
class Test_Example_BasicHtml(unittest.TestCase):
13+
"""
14+
Ensure run_example produces expected HTML files for the example input.
15+
"""
16+
17+
DIR_TREE = None
18+
19+
@classmethod
20+
def setUpClass(cls):
21+
args = SimpleNamespace(
22+
dir=SCRATCH_DIR,
23+
filename="examples/30001393.bin",
24+
excavator="examples.showcase",
25+
)
26+
shutil.rmtree(args.dir, ignore_errors=True)
27+
os.makedirs(args.dir, exist_ok=True)
28+
ctx = perform_excavation(args)
29+
ctx.produce_html()
30+
cls.DIR_TREE = list(os.walk(args.dir))
31+
32+
def toplevel(self):
33+
return self.__class__.DIR_TREE[0]
34+
35+
def toplevel_dirnames(self):
36+
_, dirs, __ = self.toplevel()
37+
dirs.sort()
38+
return dirs
39+
40+
def toplevel_filenames(self):
41+
_, __, filenames = self.toplevel()
42+
return filenames
43+
44+
def test_produces_top_level_index(self):
45+
toplevel_filenames = self.toplevel_filenames()
46+
self.assertTrue("index.html" in toplevel_filenames)
47+
self.assertTrue("index.css" in toplevel_filenames)
48+
49+
def test_produces_digest_directories(self):
50+
toplevel_dirnames = self.toplevel_dirnames()
51+
self.assertEqual(toplevel_dirnames, ['08', '79', 'fa'])

0 commit comments

Comments
 (0)