Skip to content

Commit 0d7068a

Browse files
committed
Cleaner command support
1 parent 1918e4a commit 0d7068a

File tree

4 files changed

+129
-60
lines changed

4 files changed

+129
-60
lines changed

demosys/core/management/__init__.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,26 +19,26 @@ def local_command_dir():
1919
return command_dir
2020

2121

22-
def load_command_class(name):
23-
return import_module('demosys.core.management.commands.{}'.format(name))
22+
def load_command_class(app_name, name):
23+
module = import_module(f'{app_name}.management.commands.{name}')
24+
return module.Command()
2425

2526

2627
def execute_from_command_line(argv=None):
28+
"""
29+
Currently the only entrypoint (manage.py, demosys-admin)
30+
"""
2731
if not argv:
2832
argv = sys.argv
33+
34+
# prog_name = argv[0]
2935
commands = find_commands(local_command_dir())
3036
command = argv[1] if len(argv) > 1 else None
3137

32-
source = os.path.basename(argv[0])
33-
if source == "demosys-admin":
34-
print("Running as demosys-admin")
35-
3638
if command in commands:
37-
module = load_command_class(command)
38-
if hasattr(module, 'run'):
39-
module.run(argv[2:])
40-
else:
41-
print("Command {} is not runnable".format(command))
39+
cmd = load_command_class('demosys.core', command)
40+
cmd.run_from_argv(argv)
4241
else:
4342
print("Available commands:")
44-
print("\n".join(commands))
43+
for c in commands:
44+
print(f" - {c}")

demosys/core/management/base.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import argparse
2+
from importlib import import_module
3+
4+
5+
class CommandError(Exception):
6+
pass
7+
8+
9+
class BaseCommand:
10+
help = ''
11+
12+
def __init__(self):
13+
"""should take cmd config"""
14+
pass
15+
16+
def add_arguments(self, parser):
17+
pass
18+
19+
def handle(self, *args, **options):
20+
raise NotImplementedError
21+
22+
def run_from_argv(self, argv):
23+
parser = self.create_parser(argv[0], argv[1])
24+
options = parser.parse_args(argv[2:])
25+
cmd_options = vars(options)
26+
args = cmd_options.pop('args', ())
27+
self.handle(*args, **cmd_options)
28+
29+
def print_help(self, prog_name, subcommand):
30+
parser = self.create_parser(prog_name, subcommand)
31+
parser.print_help()
32+
33+
def create_parser(self, prog_name, subcommand):
34+
"""
35+
Create argument parser and deal with ``add_arguments``.
36+
:param prog_name: Name of the command (argv[0])
37+
:return: ArgumentParser
38+
"""
39+
parser = argparse.ArgumentParser(prog_name, subcommand)
40+
# Add generic arguments here
41+
self.add_arguments(parser)
42+
return parser
43+
44+
45+
class CreateCommand(BaseCommand):
46+
"""Used for createproject and createeffect"""
47+
48+
def validate_name(self, name):
49+
"""Can the name be used as a python module or package?"""
50+
if not name:
51+
raise ValueError("Name cannot be empty")
52+
53+
# Can the name be used as an identifier in python (module or package name)
54+
if not name.isidentifier():
55+
raise ValueError(f"{name} is not a valid identifier")
56+
57+
def try_import(self, name):
58+
"""Attemt to import the name"""
59+
try:
60+
import_module(name)
61+
except ImportError:
62+
pass
63+
else:
64+
raise ValueError(f"{name} conflicts with an existing python module")
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import os
2+
from demosys.core.management.base import CreateCommand
3+
4+
5+
class Command(CreateCommand):
6+
help = "Create an effect"
7+
8+
def add_arguments(self, parser):
9+
parser.add_argument("name", help="Name of the effect")
10+
11+
def handle(self, *args, **options):
12+
name = options['name']
13+
14+
# Check for python module collision
15+
self.try_import(name)
16+
17+
# Is the name a valid identifier?
18+
self.validate_name(name)
19+
20+
# Make sure we don't mess with existing directories
21+
if os.path.exists(name):
22+
print(f"Directory {name} already exist. Aborting.")
23+
return

demosys/core/management/commands/createproject.py

Lines changed: 30 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,47 @@
1-
# FIXME: Rewrite this into something less horrible in the future
2-
# Right now we just want this to work
31
import os
4-
from importlib import import_module
2+
from demosys.core.management.base import CreateCommand
53

6-
HELP = "Create a project"
74

5+
class Command(CreateCommand):
6+
help = "Create a project"
87

9-
def print_usage():
10-
print("Usage:")
11-
print(" crateproject <name>")
8+
def add_arguments(self, parser):
9+
parser.add_argument("name", help="Name of the project")
1210

11+
def handle(self, *args, **options):
12+
name = options['name']
1313

14-
def run(args):
15-
if not args:
16-
print_usage()
17-
return
14+
# Check for python module collision
15+
self.try_import(name)
1816

19-
name = args[0]
20-
print(f"Creating project '{name}'")
17+
# Is the name a valid identifier?
18+
self.validate_name(name)
2119

22-
# Check for python module collision
23-
try:
24-
import_module(name)
25-
except ImportError:
26-
pass
27-
else:
28-
raise ValueError(f"{name} conflicts with an existing python module")
20+
# Make sure we don't mess with existing directories
21+
if os.path.exists(name):
22+
print(f"Directory {name} already exist. Aborting.")
23+
return
2924

30-
# Is the name a valid identifier?
31-
validate_name(name)
25+
manage_file = 'manage.py'
26+
if os.path.exists(manage_file):
27+
print("A manage.py file already exist in the current directory. Aborting.")
28+
return
3229

33-
# Make sure we don't mess with existing directories
34-
if os.path.exists(name):
35-
print(f"Directory {name} already exist. Aborting.")
36-
return
30+
# Create the project directory
31+
os.makedirs(name)
3732

38-
# Create the project directory
39-
os.makedirs(name)
33+
# Use the default settings file
34+
os.environ['DEMOSYS_SETTINGS_MODULE'] = 'demosys.conf.default_settings'
35+
from demosys.conf import settings
36+
from demosys.conf import settingsfile
4037

41-
# Use the default settings file
42-
os.environ['DEMOSYS_SETTINGS_MODULE'] = 'demosys.conf.default_settings'
43-
from demosys.conf import settings
44-
from demosys.conf import settingsfile
38+
with open(os.path.join(name, 'settings.py'), 'w') as fd:
39+
fd.write(settingsfile.create(settings))
4540

46-
with open(os.path.join(name, 'settings.py'), 'w') as fd:
47-
fd.write(settingsfile.create(settings))
41+
with open(manage_file, 'w') as fd:
42+
fd.write(gen_manage_py(name))
4843

49-
manage_file = 'manage.py'
50-
with open(manage_file, 'w') as fd:
51-
fd.write(gen_manage_py(name))
52-
53-
os.chmod(manage_file, 0o777)
54-
55-
56-
def validate_name(name):
57-
if not name:
58-
raise ValueError("Name cannot be empty")
59-
60-
# Can the name be used as an identifier in python (module or package name)
61-
if not name.isidentifier():
62-
raise ValueError(f"{name} is not a valid identifier")
44+
os.chmod(manage_file, 0o777)
6345

6446

6547
def gen_manage_py(project_name):

0 commit comments

Comments
 (0)