Coverage for /home/ubuntu/hidebound/python/hidebound/core/config.py: 100%
65 statements
« prev ^ index » next coverage.py v7.5.4, created at 2024-07-05 23:50 +0000
« prev ^ index » next coverage.py v7.5.4, created at 2024-07-05 23:50 +0000
1from typing import Union # noqa F401
3from copy import copy
4from importlib import import_module
5import inspect
6import os
7from pathlib import Path
8import sys
10from schematics.exceptions import ValidationError
11from schematics.types import (
12 BaseType, BooleanType, DictType, IntType, ListType, ModelType, StringType,
13 URLType
14)
15from schematics import Model
17from hidebound.core.connection import DaskConnectionConfig
18from hidebound.core.specification_base import SpecificationBase
19from hidebound.exporters.disk_exporter import DiskConfig
20from hidebound.exporters.girder_exporter import GirderConfig
21from hidebound.exporters.s3_exporter import S3Config
22import hidebound.core.validators as vd
23# ------------------------------------------------------------------------------
26# must be in here to avoid circular import
27def is_specification_file(filepath):
28 # type: (Union[str, Path]) -> None
29 '''
30 Validator for specification files given to Database.
32 Args:
33 filepath (str or Path): Filepath of python specification file.
35 Raises:
36 ValidationError: If module could not be imported.
37 ValidationError: If module has no SPECIFICATIONS attribute.
38 ValidationError: If module SPECIFICATIONS attribute is not a list.
39 ValidationError: If modules classes in SPECIFICATIONS attribute are not
40 subclasses of SpecificationBase.
41 ValidationError: If keys in SPECIFICATIONS attribute are not lowercase
42 versions of class names.
43 '''
44 # get module name
45 filepath = Path(filepath)
46 filename = filepath.name
47 filename, _ = os.path.splitext(filename)
49 # append module parent to sys.path
50 parent = filepath.parent.as_posix()
51 temp = copy(sys.path)
52 sys.path.append(parent)
54 # import module
55 mod = None
56 try:
57 mod = import_module(filename, filepath) # type: ignore
58 except Exception:
59 sys.path = temp
60 msg = f'{filepath.as_posix()} could not be imported.'
61 raise ValidationError(msg)
63 # get SPECIFICATIONS
64 if not hasattr(mod, 'SPECIFICATIONS'):
65 sys.path = temp
66 msg = f'{filepath.as_posix()} has no SPECIFICATIONS attribute.'
67 raise ValidationError(msg)
69 # ensure SPECIFICATIONS is a list
70 if not isinstance(mod.SPECIFICATIONS, list):
71 sys.path = temp
72 msg = f'{filepath.as_posix()} SPECIFICATIONS attribute is not a list.'
73 raise ValidationError(msg)
75 # ensure all SPECIFICATIONS are subclasses of SpecificationBase
76 errors = list(filter(
77 lambda x: not inspect.isclass(x) or not issubclass(x, SpecificationBase),
78 mod.SPECIFICATIONS
79 ))
80 if len(errors) > 0:
81 sys.path = temp
82 msg = f'{errors} are not subclasses of SpecificationBase.'
83 raise ValidationError(msg)
85 sys.path = temp
86# ------------------------------------------------------------------------------
89class Config(Model):
90 r'''
91 A class for validating configurations supplied to Database.
93 Attributes:
94 ingress_directory (str or Path): Root directory to recurse.
95 staging_directory (str or Path): Directory where hidebound data will be
96 staged.
97 include_regex (str, optional): Include filenames that match this regex.
98 Default: ''.
99 exclude_regex (str, optional): Exclude filenames that match this regex.
100 Default: '\.DS_Store'.
101 write_mode (str, optional): How assets will be extracted to
102 hidebound/content directory. Default: copy.
103 workflow (list[str], optional): Ordered steps of workflow. Default:
104 ['delete', 'update', 'create', 'export'].
105 redact_regex (str, optional): Regex pattern matched to config keys.
106 Values of matching keys will be redacted.
107 Default: "(_key|_id|_token|url)$".
108 redact_hash (bool, optional): Whether to replace redacted values with
109 "REDACTED" or a hash of the value. Default: True.
110 specification_files (list[str], optional): List of asset specification
111 files. Default: [].
112 exporters (dict, optional): Dictionary of exporter configs, where the
113 key is the exporter name and the value is its config. Default: {}.
114 webhooks (list[dict], optional): List of webhooks to be called after
115 export. Default: [].
116 dask (dict, optional). Dask configuration. Default: {}.
117 '''
118 ingress_directory = StringType(
119 required=True, validators=[vd.is_directory]
120 ) # type: StringType
121 staging_directory = StringType(
122 required=True, validators=[vd.is_directory, vd.is_hidebound_directory]
123 ) # type: StringType
124 include_regex = StringType(default='', required=True) # type: StringType
125 exclude_regex = StringType(default=r'\.DS_Store', required=True) # type: StringType
126 write_mode = StringType(
127 required=True,
128 validators=[lambda x: vd.is_in(x, ['copy', 'move'])],
129 default="copy",
130 ) # type: StringType
131 dask = ModelType(
132 DaskConnectionConfig, default={}, required=True
133 ) # type: ModelType
134 workflow = ListType(
135 StringType(),
136 required=True,
137 validators=[vd.is_workflow],
138 default=['delete', 'update', 'create', 'export']
139 ) # type: ListType
140 redact_regex = StringType(required=True, default='(_key|_id|_token|url)$') # type: StringType
141 redact_hash = BooleanType(required=True, default=True) # type: BooleanType
142 specification_files = ListType(
143 StringType(validators=[is_specification_file, vd.is_file]),
144 default=[],
145 required=True
146 ) # type: ListType
147 exporters = ListType(
148 BaseType(
149 validators=[lambda x: vd.is_one_of(
150 x, [DiskConfig, S3Config, GirderConfig]
151 )]
152 ),
153 required=False,
154 default=[],
155 ) # type: ListType
157 class WebhookConfig(Model):
158 url = URLType(required=True, fqdn=False) # type: URLType
159 method = StringType(required=True, validators=[vd.is_http_method]) # type: StringType
160 headers = DictType(StringType, required=False) # type: DictType
161 data = DictType(BaseType, required=False, serialize_when_none=False) # type: DictType
162 json = DictType(BaseType, required=False, serialize_when_none=False) # type: DictType
163 params = DictType(BaseType, required=False, serialize_when_none=False) # type: DictType
164 timeout = IntType(required=False, serialize_when_none=False) # type: IntType
165 webhooks = ListType(ModelType(WebhookConfig), required=False, default=[]) # type: ListType