Skip to content

Commit 10f9507

Browse files
committed
Add decorators module ...
Moves the operations_mode decorator into the new module. Adds another decorator for checking the python system path and adding any paths specified in our designated .pth file. This applies for any file that might need access to the hub-parsers
1 parent 40ac562 commit 10f9507

File tree

2 files changed

+123
-60
lines changed

2 files changed

+123
-60
lines changed
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
"""
2+
Collection of decorators for usage within the biothings-cli
3+
4+
These are often method we want associated with many of the plugin methods we
5+
use, but don't directly impact the logic of the actual operation. Typically things
6+
related to paths and configurations that apply to large swaths of the cli
7+
would make sense as a decorator
8+
"""
9+
10+
import functools
11+
import inspect
12+
import logging
13+
import pathlib
14+
import sys
15+
from typing import Callable
16+
17+
from biothings.cli.exceptions import MissingPluginName
18+
19+
20+
logger = logging.getLogger(name="biothings-cli")
21+
22+
23+
def operation_mode(operation_method: Callable):
24+
"""
25+
Based off the directory structure for where the biothings-cli
26+
was invoked we set the "mode" to one of two states:
27+
28+
0) singular
29+
The current working directory contains a singular data-plugin
30+
31+
In this case we don't require a plugin_name argument to be passed
32+
at the command-line
33+
34+
1) hub
35+
The current working directory contains N directories operating as a
36+
"hub" or collection of data-plugins under one umbrella
37+
38+
In this case we do require a plugin_name argument to be passed
39+
at the command-line. Otherwise we have no idea which data-plugin to
40+
refer to
41+
42+
We attempt to load the plugin from this working directory. If we sucessfully load
43+
either a manifest or advanced plugin, then we can safely say this is a singular
44+
dataplugin
45+
46+
If we cannot load either a manifest or advanced plugin then we default assume that
47+
the mode is hub
48+
"""
49+
50+
@functools.wraps(operation_method)
51+
async def determine_operation_mode(*args, **kwargs):
52+
working_directory = pathlib.Path.cwd()
53+
working_directory_files = {file.name for file in working_directory.iterdir()}
54+
55+
mode = None
56+
if "manifest.json" in working_directory_files or "manifest.yaml" in working_directory_files:
57+
logger.debug("Inferring singular manifest plugin from directory structure")
58+
mode = "SINGULAR"
59+
elif "__init__.py" in working_directory_files:
60+
logger.debug("Inferring singular advanced plugin from directory structure")
61+
mode = "SINGULAR"
62+
else:
63+
logger.debug("Inferring multiple plugins from directory structure")
64+
mode = "HUB"
65+
66+
if mode == "SINGULAR":
67+
if kwargs.get("plugin_name", None) is not None:
68+
kwargs["plugin_name"] = None
69+
elif mode == "HUB":
70+
if kwargs.get("plugin_name", None) is None:
71+
raise MissingPluginName(working_directory)
72+
73+
if inspect.iscoroutinefunction(operation_method):
74+
operation_result = await operation_method(*args, **kwargs)
75+
else:
76+
operation_result = operation_method(*args, **kwargs)
77+
return operation_result
78+
79+
return determine_operation_mode
80+
81+
82+
def cli_system_path(func: Callable): # pylint: disable=unused-argument
83+
"""
84+
Used for ensuring that if we've appended files to biothings-cli
85+
path file (stored under config.BIOTHINGS_CLI_PATH), then we need to update
86+
the system path so we can discover the modules at runtime
87+
"""
88+
89+
@functools.wraps(func)
90+
async def update_system_path(*args, **kwargs):
91+
from biothings import config
92+
93+
discovery_path = pathlib.Path(config.BIOTHINGS_CLI_PATH).resolve().absolute()
94+
path_file = discovery_path.joinpath(".biothings_cli.pth")
95+
96+
if path_file.exists():
97+
with open(path_file, "r", encoding="utf-8") as handle:
98+
path_entries = handle.readlines()
99+
path_entries = [entry.strip("\n") for entry in path_entries]
100+
sys.path.extend(path_entries)
101+
for path in path_entries:
102+
logger.debug("Adding %s to system path", path)
103+
104+
if inspect.iscoroutinefunction(func):
105+
func_result = await func(*args, **kwargs)
106+
else:
107+
func_result = func(*args, **kwargs)
108+
return func_result
109+
110+
return update_system_path

biothings/cli/commands/operations.py

Lines changed: 13 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@
4747
"""
4848

4949
import asyncio
50-
import functools
5150
import logging
5251
import multiprocessing
5352
import os
@@ -57,7 +56,7 @@
5756
import shutil
5857
import sys
5958
import uuid
60-
from typing import Callable, Optional, Union
59+
from typing import Optional, Union
6160

6261
import jsonschema
6362
import rich
@@ -67,8 +66,9 @@
6766
from rich.console import Console
6867
from rich.panel import Panel
6968

69+
from biothings.cli.commands.decorators import cli_system_path, operation_mode
7070
from biothings.cli.structure import TEMPLATE_DIRECTORY
71-
from biothings.cli.exceptions import MissingPluginName, UnknownUploaderSource
71+
from biothings.cli.exceptions import UnknownUploaderSource
7272
from biothings.cli.utils import (
7373
clean_dumped_files,
7474
clean_uploaded_sources,
@@ -87,65 +87,8 @@
8787
logger = logging.getLogger(name="biothings-cli")
8888

8989

90-
def operation_mode(operation_method: Callable):
91-
"""
92-
Based off the directory structure for where the biothings-cli
93-
was invoked we set the "mode" to one of two states:
94-
95-
0) singular
96-
The current working directory contains a singular data-plugin
97-
98-
In this case we don't require a plugin_name argument to be passed
99-
at the command-line
100-
101-
1) hub
102-
The current working directory contains N directories operating as a
103-
"hub" or collection of data-plugins under one umbrella
104-
105-
In this case we do require a plugin_name argument to be passed
106-
at the command-line. Otherwise we have no idea which data-plugin to
107-
refer to
108-
109-
We attempt to load the plugin from this working directory. If we sucessfully load
110-
either a manifest or advanced plugin, then we can safely say this is a singular
111-
dataplugin
112-
113-
If we cannot load either a manifest or advanced plugin then we default assume that
114-
the mode is hub
115-
"""
116-
117-
@functools.wraps(operation_method)
118-
def determine_operation_mode(*args, **kwargs):
119-
working_directory = pathlib.Path.cwd()
120-
working_directory_files = {file.name for file in working_directory.iterdir()}
121-
122-
mode = None
123-
if "manifest.json" in working_directory_files or "manifest.yaml" in working_directory_files:
124-
logger.debug("Inferring singular manifest plugin from directory structure")
125-
mode = "SINGULAR"
126-
elif "__init__.py" in working_directory_files:
127-
logger.debug("Inferring singular advanced plugin from directory structure")
128-
mode = "SINGULAR"
129-
else:
130-
logger.debug("Inferring multiple plugins from directory structure")
131-
mode = "HUB"
132-
133-
if mode == "SINGULAR":
134-
if kwargs.get("plugin_name", None) is not None:
135-
kwargs["plugin_name"] = None
136-
elif mode == "HUB":
137-
if kwargs.get("plugin_name", None) is None:
138-
raise MissingPluginName(working_directory)
139-
140-
operation_result = operation_method(*args, **kwargs)
141-
return operation_result
142-
143-
return determine_operation_mode
144-
145-
14690
# do not apply operation_mode decorator since this operation means to create a new plugin
14791
# regardless what the current working directory has
148-
# @operation_mode
14992
def do_create(plugin_name: str, multi_uploaders: bool = False, parallelizer: bool = False):
15093
"""
15194
Create a new data plugin from the template
@@ -178,6 +121,7 @@ def do_create(plugin_name: str, multi_uploaders: bool = False, parallelizer: boo
178121
logger.info("Successfully created data plugin template at: %s\n", new_plugin_directory)
179122

180123

124+
@cli_system_path
181125
@operation_mode
182126
async def do_dump(plugin_name: Optional[str] = None, show_dumped: bool = True) -> None:
183127
"""
@@ -223,6 +167,7 @@ async def do_dump(plugin_name: Optional[str] = None, show_dumped: bool = True) -
223167
show_dumped_files(data_folder, assistant_instance.plugin_name)
224168

225169

170+
@cli_system_path
226171
@operation_mode
227172
async def do_upload(plugin_name: Optional[str] = None, batch_limit: int = 10000, show_uploaded: bool = True) -> None:
228173
"""
@@ -277,6 +222,7 @@ async def do_upload(plugin_name: Optional[str] = None, batch_limit: int = 10000,
277222
show_uploaded_sources(pathlib.Path(assistant_instance.plugin_directory), assistant_instance.plugin_name)
278223

279224

225+
@cli_system_path
280226
@operation_mode
281227
async def do_parallel_upload(
282228
plugin_name: Optional[str] = None, batch_limit: int = 10000, show_uploaded: bool = True
@@ -344,6 +290,7 @@ async def do_parallel_upload(
344290
show_uploaded_sources(pathlib.Path(assistant_instance.plugin_directory), assistant_instance.plugin_name)
345291

346292

293+
@cli_system_path
347294
@operation_mode
348295
async def do_dump_and_upload(plugin_name: str) -> None:
349296
"""
@@ -354,6 +301,7 @@ async def do_dump_and_upload(plugin_name: str) -> None:
354301
logger.info("[green]Success![/green] :rocket:", extra={"markup": True})
355302

356303

304+
@cli_system_path
357305
@operation_mode
358306
async def do_index(plugin_name: Optional[str] = None, sub_source_name: Optional[str] = None) -> None:
359307
"""
@@ -540,6 +488,7 @@ async def do_index(plugin_name: Optional[str] = None, sub_source_name: Optional[
540488
await show_source_index(index_name, assistant_instance.index_manager, elasticsearch_mapping)
541489

542490

491+
@cli_system_path
543492
@operation_mode
544493
async def do_list(
545494
plugin_name: Optional[str] = None, dump: bool = True, upload: bool = True, hubdb: bool = False
@@ -569,6 +518,7 @@ async def do_list(
569518
show_hubdb_content()
570519

571520

521+
@cli_system_path
572522
@operation_mode
573523
async def do_inspect(
574524
plugin_name: Optional[str] = None,
@@ -633,6 +583,7 @@ async def do_inspect(
633583
write_mapping_to_file(sub_output, inspection_mapping)
634584

635585

586+
@cli_system_path
636587
@operation_mode
637588
async def do_serve(plugin_name: Optional[str] = None, host: str = "localhost", port: int = 9999):
638589
"""
@@ -651,6 +602,7 @@ async def do_serve(plugin_name: Optional[str] = None, host: str = "localhost", p
651602
await main(host=host, port=port, db=src_db, table_space=table_space)
652603

653604

605+
@cli_system_path
654606
@operation_mode
655607
async def do_clean(
656608
plugin_name: Optional[str] = None, dump: bool = False, upload: bool = False, clean_all: bool = False
@@ -714,6 +666,7 @@ async def display_schema():
714666
console.print(panel)
715667

716668

669+
@cli_system_path
717670
@operation_mode
718671
async def validate_manifest(plugin_name: Optional[str] = None):
719672
"""

0 commit comments

Comments
 (0)