clams-python 1.3.0__tar.gz → 1.3.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {clams_python-1.3.0/clams_python.egg-info → clams_python-1.3.2}/PKG-INFO +19 -8
- clams_python-1.3.2/VERSION +1 -0
- clams_python-1.3.2/clams/__init__.py +54 -0
- {clams_python-1.3.0 → clams_python-1.3.2}/clams/app/__init__.py +8 -1
- {clams_python-1.3.0 → clams_python-1.3.2}/clams/appmetadata/__init__.py +102 -61
- {clams_python-1.3.0 → clams_python-1.3.2}/clams/develop/templates/app/metadata.py.template +2 -1
- clams_python-1.3.2/clams/ver/__init__.py +1 -0
- {clams_python-1.3.0 → clams_python-1.3.2/clams_python.egg-info}/PKG-INFO +19 -8
- {clams_python-1.3.0 → clams_python-1.3.2}/clams_python.egg-info/SOURCES.txt +3 -7
- {clams_python-1.3.0 → clams_python-1.3.2}/clams_python.egg-info/requires.txt +2 -2
- {clams_python-1.3.0 → clams_python-1.3.2}/requirements.txt +2 -2
- {clams_python-1.3.0 → clams_python-1.3.2}/setup.py +5 -23
- clams_python-1.3.0/VERSION +0 -1
- clams_python-1.3.0/clams/__init__.py +0 -49
- clams_python-1.3.0/clams/mmif_utils/__init__.py +0 -3
- clams_python-1.3.0/clams/mmif_utils/rewind.py +0 -114
- clams_python-1.3.0/clams/mmif_utils/source.py +0 -293
- clams_python-1.3.0/clams/ver/__init__.py +0 -1
- clams_python-1.3.0/tests/test_clamscli.py +0 -220
- {clams_python-1.3.0 → clams_python-1.3.2}/LICENSE +0 -0
- {clams_python-1.3.0 → clams_python-1.3.2}/MANIFEST.in +0 -0
- {clams_python-1.3.0 → clams_python-1.3.2}/README.md +0 -0
- {clams_python-1.3.0 → clams_python-1.3.2}/clams/develop/__init__.py +0 -0
- {clams_python-1.3.0 → clams_python-1.3.2}/clams/develop/templates/app/.dockerignore.template +0 -0
- {clams_python-1.3.0 → clams_python-1.3.2}/clams/develop/templates/app/.gitignore.template +0 -0
- {clams_python-1.3.0 → clams_python-1.3.2}/clams/develop/templates/app/Containerfile.template +0 -0
- {clams_python-1.3.0 → clams_python-1.3.2}/clams/develop/templates/app/LICENSE.template +0 -0
- {clams_python-1.3.0 → clams_python-1.3.2}/clams/develop/templates/app/README.md.template +0 -0
- {clams_python-1.3.0 → clams_python-1.3.2}/clams/develop/templates/app/app.py.template +0 -0
- {clams_python-1.3.0 → clams_python-1.3.2}/clams/develop/templates/app/cli.py.template +0 -0
- {clams_python-1.3.0 → clams_python-1.3.2}/clams/develop/templates/app/requirements.txt.template +0 -0
- {clams_python-1.3.0 → clams_python-1.3.2}/clams/develop/templates/gha/for-clams-team.md.template +0 -0
- {clams_python-1.3.0 → clams_python-1.3.2}/clams/develop/templates/gha/workflows/issue-apps-project.yml.template +0 -0
- {clams_python-1.3.0 → clams_python-1.3.2}/clams/develop/templates/gha/workflows/issue-assign.yml.template +0 -0
- {clams_python-1.3.0 → clams_python-1.3.2}/clams/develop/templates/gha/workflows/issue-close.yml.template +0 -0
- {clams_python-1.3.0 → clams_python-1.3.2}/clams/develop/templates/gha/workflows/publish.yml.template +0 -0
- {clams_python-1.3.0 → clams_python-1.3.2}/clams/restify/__init__.py +0 -0
- {clams_python-1.3.0 → clams_python-1.3.2}/clams/serve/__init__.py +0 -0
- {clams_python-1.3.0 → clams_python-1.3.2}/clams_python.egg-info/dependency_links.txt +0 -0
- {clams_python-1.3.0 → clams_python-1.3.2}/clams_python.egg-info/entry_points.txt +0 -0
- {clams_python-1.3.0 → clams_python-1.3.2}/clams_python.egg-info/top_level.txt +0 -0
- {clams_python-1.3.0 → clams_python-1.3.2}/setup.cfg +0 -0
- {clams_python-1.3.0 → clams_python-1.3.2}/tests/test_clamsapp.py +0 -0
|
@@ -1,26 +1,37 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: clams-python
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.2
|
|
4
4
|
Summary: A collection of APIs to develop CLAMS app for python
|
|
5
5
|
Home-page: https://clams.ai
|
|
6
6
|
Author: Brandeis Lab for Linguistics and Computation
|
|
7
7
|
Author-email: admin@clams.ai
|
|
8
|
-
|
|
8
|
+
License: Apache-2.0
|
|
9
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
9
10
|
Classifier: Framework :: Flask
|
|
10
11
|
Classifier: Framework :: Pytest
|
|
11
|
-
Classifier: Intended Audience :: Developers
|
|
12
|
-
Classifier: License :: OSI Approved :: Apache Software License
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
13
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
14
|
-
Requires-Python: >=3.
|
|
14
|
+
Requires-Python: >=3.10
|
|
15
15
|
Description-Content-Type: text/markdown
|
|
16
16
|
License-File: LICENSE
|
|
17
|
-
Requires-Dist: mmif-python==1.
|
|
17
|
+
Requires-Dist: mmif-python==1.1.1
|
|
18
18
|
Requires-Dist: Flask>=2
|
|
19
19
|
Requires-Dist: Flask-RESTful>=0.3.9
|
|
20
20
|
Requires-Dist: gunicorn>=20
|
|
21
21
|
Requires-Dist: lapps>=0.0.2
|
|
22
|
-
Requires-Dist: pydantic
|
|
22
|
+
Requires-Dist: pydantic>=2
|
|
23
23
|
Requires-Dist: jsonschema>=3
|
|
24
|
+
Dynamic: author
|
|
25
|
+
Dynamic: author-email
|
|
26
|
+
Dynamic: classifier
|
|
27
|
+
Dynamic: description
|
|
28
|
+
Dynamic: description-content-type
|
|
29
|
+
Dynamic: home-page
|
|
30
|
+
Dynamic: license
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
Dynamic: requires-dist
|
|
33
|
+
Dynamic: requires-python
|
|
34
|
+
Dynamic: summary
|
|
24
35
|
|
|
25
36
|
## CLAMS project
|
|
26
37
|
[CLAMS project](https://clams.ai) aims at free and open-source software platform for computational analysis and metadata generation applications for multimedia material. We believe free and open AI-based multimedia processing platform can bring many benefits of technical advancement to libraries, archives, and museums. To achieve interoperability between various computational applications developed be different vendors, which is absolutely necessary for using applications together supported by user-friendly workflow engines, we are also developing JSON-based MultiMedia Interchange Format ([MMIF](https://mmif.clams.ai))
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
1.3.2
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
import mmif
|
|
5
|
+
from clams import develop
|
|
6
|
+
from clams.app import *
|
|
7
|
+
from clams.app import __all__ as app_all
|
|
8
|
+
from clams.appmetadata import AppMetadata
|
|
9
|
+
from clams.restify import Restifier
|
|
10
|
+
from clams.ver import __version__
|
|
11
|
+
|
|
12
|
+
__all__ = [AppMetadata, Restifier] + app_all
|
|
13
|
+
version_template = "{} (based on MMIF spec: {})"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def prep_argparser():
|
|
17
|
+
parser = argparse.ArgumentParser()
|
|
18
|
+
parser.add_argument(
|
|
19
|
+
'-v', '--version',
|
|
20
|
+
action='version',
|
|
21
|
+
version=version_template.format(__version__, mmif.__specver__)
|
|
22
|
+
)
|
|
23
|
+
subparsers = parser.add_subparsers(title='sub-command', dest='subcmd')
|
|
24
|
+
return parser, subparsers
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def cli():
|
|
28
|
+
parser, subparsers = prep_argparser()
|
|
29
|
+
cli_modules = {}
|
|
30
|
+
# thinly wrap all `mmif` subcommands
|
|
31
|
+
# this is primarily for backward compatibility for `souce` and `rewind` subcmds
|
|
32
|
+
to_register = list(mmif.find_all_modules('mmif.utils.cli'))
|
|
33
|
+
# then add my own subcommands
|
|
34
|
+
to_register.append(develop)
|
|
35
|
+
for cli_module in to_register:
|
|
36
|
+
cli_module_name = cli_module.__name__.rsplit('.')[-1]
|
|
37
|
+
cli_modules[cli_module_name] = cli_module
|
|
38
|
+
subcmd_parser = cli_module.prep_argparser(add_help=False)
|
|
39
|
+
subparsers.add_parser(cli_module_name, parents=[subcmd_parser],
|
|
40
|
+
help=cli_module.describe_argparser()[0],
|
|
41
|
+
description=cli_module.describe_argparser()[1],
|
|
42
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
43
|
+
)
|
|
44
|
+
if len(sys.argv) == 1:
|
|
45
|
+
parser.print_help(sys.stderr)
|
|
46
|
+
sys.exit(1)
|
|
47
|
+
args = parser.parse_args()
|
|
48
|
+
if args.subcmd not in cli_modules:
|
|
49
|
+
parser.print_help(sys.stderr)
|
|
50
|
+
else:
|
|
51
|
+
cli_modules[args.subcmd].main(args)
|
|
52
|
+
|
|
53
|
+
if __name__ == '__main__':
|
|
54
|
+
cli()
|
|
@@ -31,6 +31,7 @@ falsy_values = [
|
|
|
31
31
|
False
|
|
32
32
|
]
|
|
33
33
|
|
|
34
|
+
|
|
34
35
|
class ClamsApp(ABC):
|
|
35
36
|
"""
|
|
36
37
|
An abstract class to define API's for ClamsApps. A CLAMS app should inherit
|
|
@@ -403,7 +404,13 @@ class ParameterCaster(object):
|
|
|
403
404
|
if valuetype == dict:
|
|
404
405
|
casted.setdefault(k, {}).update(v)
|
|
405
406
|
else:
|
|
406
|
-
|
|
407
|
+
# pytype will complain about the next line, but it is actually correct
|
|
408
|
+
# casted.setdefault(k, []).append(v)
|
|
409
|
+
# so doing it in a more explicit way
|
|
410
|
+
if k in casted and isinstance(casted[k], list):
|
|
411
|
+
casted[k].append(v)
|
|
412
|
+
else:
|
|
413
|
+
casted[k] = [v]
|
|
407
414
|
else:
|
|
408
415
|
casted[k] = v
|
|
409
416
|
# when an empty value is passed (usually as a default value)
|
|
@@ -3,7 +3,7 @@ import shutil
|
|
|
3
3
|
import subprocess
|
|
4
4
|
import sys
|
|
5
5
|
from pathlib import Path
|
|
6
|
-
from typing import Union, Dict, List, Optional, Literal
|
|
6
|
+
from typing import Union, Dict, List, Optional, Literal, Any
|
|
7
7
|
|
|
8
8
|
import mmif
|
|
9
9
|
import pydantic
|
|
@@ -31,8 +31,8 @@ def get_clams_pyver():
|
|
|
31
31
|
import clams
|
|
32
32
|
return clams.__version__
|
|
33
33
|
except ImportError:
|
|
34
|
-
version_fname =
|
|
35
|
-
if
|
|
34
|
+
version_fname = Path(__file__).joinpath('../../VERSION')
|
|
35
|
+
if version_fname.exists():
|
|
36
36
|
with open(version_fname) as version_f:
|
|
37
37
|
return version_f.read().strip()
|
|
38
38
|
else:
|
|
@@ -59,13 +59,21 @@ def get_mmif_specver():
|
|
|
59
59
|
return mmif.__specver__
|
|
60
60
|
|
|
61
61
|
|
|
62
|
+
def pop_titles(js):
|
|
63
|
+
for prop in js.get('properties', {}).values():
|
|
64
|
+
prop.pop('title', None)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def jsonschema_versioning(js):
|
|
68
|
+
js['$schema'] = pydantic.json_schema.GenerateJsonSchema.schema_dialect
|
|
69
|
+
js['$comment'] = f"clams-python SDK {get_clams_pyver()} was used to generate this schema"
|
|
70
|
+
|
|
71
|
+
|
|
62
72
|
class _BaseModel(pydantic.BaseModel):
|
|
63
73
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
for prop in schema.get('properties', {}).values():
|
|
68
|
-
prop.pop('title', None)
|
|
74
|
+
model_config = {
|
|
75
|
+
"json_schema_extra": pop_titles
|
|
76
|
+
}
|
|
69
77
|
|
|
70
78
|
|
|
71
79
|
class Output(_BaseModel):
|
|
@@ -93,21 +101,53 @@ class Output(_BaseModel):
|
|
|
93
101
|
"and also can be used as a expansion specification for the type definition beyond the base "
|
|
94
102
|
"vocabulary."
|
|
95
103
|
)
|
|
96
|
-
|
|
97
|
-
|
|
104
|
+
# TODO (krim @ 5/12/21): currently there's no way to validate the property
|
|
105
|
+
# types based on vocabulary specification of an annotation type. As a result,
|
|
106
|
+
# we allow "any" type and do some basic validation below, but we need a
|
|
107
|
+
# better way for validation.
|
|
108
|
+
properties: Dict[str, Any] = pydantic.Field(
|
|
109
|
+
{},
|
|
98
110
|
description="(optional) Specification for type properties, if any. ``\"*\"`` indicates any value."
|
|
99
111
|
)
|
|
112
|
+
|
|
113
|
+
@pydantic.field_validator('properties', mode='before')
|
|
114
|
+
@classmethod
|
|
115
|
+
def validate_properties(cls, value):
|
|
116
|
+
if not isinstance(value, dict):
|
|
117
|
+
raise ValueError("Properties must be a dictionary.")
|
|
118
|
+
for key, val in value.items():
|
|
119
|
+
if not isinstance(key, str):
|
|
120
|
+
raise ValueError(f"Property key '{key}' must be a string.")
|
|
121
|
+
if isinstance(val, list):
|
|
122
|
+
if not all(isinstance(item, type(val[0])) for item in val):
|
|
123
|
+
raise ValueError(f"All elements in the list for key '{key}' must be of the same type.")
|
|
124
|
+
elif isinstance(val, dict):
|
|
125
|
+
if not all(isinstance(k, str) for k in val.keys()):
|
|
126
|
+
raise ValueError(f"All keys in the dictionary for key '{key}' must be strings.")
|
|
127
|
+
if not all(isinstance(v, type(next(iter(val.values())))) for v in val.values()):
|
|
128
|
+
raise ValueError(f"All values in the dictionary for key '{key}' must be of the same type.")
|
|
129
|
+
return value
|
|
130
|
+
|
|
131
|
+
def __init__(self, **kwargs):
|
|
132
|
+
super().__init__(**kwargs)
|
|
133
|
+
|
|
134
|
+
@pydantic.field_validator('at_type', mode='after') # because pydantic v2 doesn't auto-convert url to string
|
|
135
|
+
@classmethod
|
|
136
|
+
def stringify(cls, val):
|
|
137
|
+
return str(val)
|
|
100
138
|
|
|
101
|
-
@pydantic.
|
|
139
|
+
@pydantic.field_validator('at_type', mode='before')
|
|
140
|
+
@classmethod
|
|
102
141
|
def at_type_must_be_str(cls, v):
|
|
103
142
|
if not isinstance(v, str):
|
|
104
143
|
return str(v)
|
|
105
144
|
return v
|
|
106
145
|
|
|
107
|
-
|
|
108
|
-
title
|
|
109
|
-
extra
|
|
110
|
-
|
|
146
|
+
model_config = {
|
|
147
|
+
'title': 'CLAMS Output Specification',
|
|
148
|
+
'extra': 'forbid',
|
|
149
|
+
'validate_by_name': True,
|
|
150
|
+
}
|
|
111
151
|
|
|
112
152
|
def add_description(self, description: str):
|
|
113
153
|
"""
|
|
@@ -127,20 +167,21 @@ class Input(Output):
|
|
|
127
167
|
|
|
128
168
|
Developers should take diligent care to include all input types and their properties in the app metadata.
|
|
129
169
|
"""
|
|
130
|
-
required: bool = pydantic.Field(
|
|
170
|
+
required: Optional[bool] = pydantic.Field(
|
|
131
171
|
None,
|
|
132
172
|
description="(optional, True by default) Indicating whether this input type is mandatory or optional."
|
|
133
173
|
)
|
|
134
174
|
|
|
135
|
-
def __init__(self,
|
|
136
|
-
super().__init__(
|
|
175
|
+
def __init__(self, **kwargs):
|
|
176
|
+
super().__init__(**kwargs)
|
|
137
177
|
if self.required is None:
|
|
138
178
|
self.required = True
|
|
139
179
|
|
|
140
|
-
|
|
141
|
-
title
|
|
142
|
-
extra
|
|
143
|
-
|
|
180
|
+
model_config = {
|
|
181
|
+
'title': 'CLAMS Input Specification',
|
|
182
|
+
'extra': 'forbid',
|
|
183
|
+
'validate_by_name': True,
|
|
184
|
+
}
|
|
144
185
|
|
|
145
186
|
|
|
146
187
|
class RuntimeParameter(_BaseModel):
|
|
@@ -178,12 +219,13 @@ class RuntimeParameter(_BaseModel):
|
|
|
178
219
|
"desired dictionary is ``{'key1': 'value1', 'key2': 'value2'}``, the default value (used when "
|
|
179
220
|
"initializing a parameter) should be ``['key1:value1','key2:value2']``\n."
|
|
180
221
|
)
|
|
181
|
-
choices: List[real_valued_primitives] = pydantic.Field(
|
|
222
|
+
choices: Optional[List[real_valued_primitives]] = pydantic.Field(
|
|
182
223
|
None,
|
|
183
224
|
description="(optional) List of string values that can be accepted."
|
|
184
225
|
)
|
|
185
|
-
default: Union[real_valued_primitives, List[real_valued_primitives]] = pydantic.Field(
|
|
226
|
+
default: Optional[Union[real_valued_primitives, List[real_valued_primitives]]] = pydantic.Field(
|
|
186
227
|
None,
|
|
228
|
+
union_mode='left_to_right',
|
|
187
229
|
description="(optional) Default value for the parameter.\n\n"
|
|
188
230
|
"Notes for developers: \n\n"
|
|
189
231
|
"Setting a default value makes a parameter `optional`. \n\n"
|
|
@@ -208,9 +250,10 @@ class RuntimeParameter(_BaseModel):
|
|
|
208
250
|
if self.multivalued and self.default is not None and not isinstance(self.default, list):
|
|
209
251
|
self.default = [self.default]
|
|
210
252
|
|
|
211
|
-
|
|
212
|
-
title
|
|
213
|
-
extra
|
|
253
|
+
model_config = {
|
|
254
|
+
'title': 'CLAMS App Runtime Parameter',
|
|
255
|
+
'extra': 'forbid',
|
|
256
|
+
}
|
|
214
257
|
|
|
215
258
|
|
|
216
259
|
class AppMetadata(pydantic.BaseModel):
|
|
@@ -236,18 +279,19 @@ class AppMetadata(pydantic.BaseModel):
|
|
|
236
279
|
description="A longer description of the app (what it does, how to use, etc.)."
|
|
237
280
|
)
|
|
238
281
|
app_version: str = pydantic.Field(
|
|
239
|
-
default_factory
|
|
282
|
+
'', # instead of using default_factory, I will use model_validator to set the default value
|
|
283
|
+
# this will work around the limitation of exclude_defaults=True condition when serializing
|
|
240
284
|
description="(AUTO-GENERATED, DO NOT SET MANUALLY)\n\n"
|
|
241
285
|
"Version of the app.\n\n"
|
|
242
286
|
"When the metadata is generated using clams-python SDK, this field is automatically filled in"
|
|
243
287
|
)
|
|
244
288
|
mmif_version: str = pydantic.Field(
|
|
245
|
-
|
|
289
|
+
'', # same as above
|
|
246
290
|
description="(AUTO-GENERATED, DO NOT SET MANUALLY)\n\n"
|
|
247
291
|
"Version of MMIF specification the app.\n\n"
|
|
248
292
|
"When the metadata is generated using clams-python SDK, this field is automatically filled in."
|
|
249
293
|
)
|
|
250
|
-
analyzer_version: str = pydantic.Field(
|
|
294
|
+
analyzer_version: Optional[str] = pydantic.Field(
|
|
251
295
|
None,
|
|
252
296
|
description="(optional) Version of an analyzer software, if the app is working as a wrapper for one. "
|
|
253
297
|
)
|
|
@@ -255,7 +299,7 @@ class AppMetadata(pydantic.BaseModel):
|
|
|
255
299
|
...,
|
|
256
300
|
description="License information of the app."
|
|
257
301
|
)
|
|
258
|
-
analyzer_license: str = pydantic.Field(
|
|
302
|
+
analyzer_license: Optional[str] = pydantic.Field(
|
|
259
303
|
None,
|
|
260
304
|
description="(optional) License information of an analyzer software, if the app works as a wrapper for one. "
|
|
261
305
|
)
|
|
@@ -298,7 +342,7 @@ class AppMetadata(pydantic.BaseModel):
|
|
|
298
342
|
[],
|
|
299
343
|
description="List of runtime parameters. Can be empty."
|
|
300
344
|
)
|
|
301
|
-
dependencies: List[str] = pydantic.Field(
|
|
345
|
+
dependencies: Optional[List[str]] = pydantic.Field(
|
|
302
346
|
None,
|
|
303
347
|
description="(optional) List of software dependencies of the app. \n\n"
|
|
304
348
|
"This list is completely optional, as in most cases such dependencies are specified in a separate "
|
|
@@ -307,36 +351,38 @@ class AppMetadata(pydantic.BaseModel):
|
|
|
307
351
|
"List items must be strings, not any kind of structured data. Thus, it is recommended to include "
|
|
308
352
|
"a package name and its version in the string value at the minimum (e.g., ``clams-python==1.2.3``)."
|
|
309
353
|
)
|
|
310
|
-
more: Dict[str, str] = pydantic.Field(
|
|
354
|
+
more: Optional[Dict[str, str]] = pydantic.Field(
|
|
311
355
|
None,
|
|
312
356
|
description="(optional) A string-to-string map that can be used to store any additional metadata of the app."
|
|
313
357
|
)
|
|
314
358
|
|
|
315
|
-
|
|
316
|
-
title
|
|
317
|
-
extra
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
359
|
+
model_config = {
|
|
360
|
+
'title': 'CLAMS AppMetadata',
|
|
361
|
+
'extra': 'forbid',
|
|
362
|
+
'validate_by_name': True,
|
|
363
|
+
'json_schema_extra': lambda schema, model: [adjust(schema) for adjust in [pop_titles, jsonschema_versioning]],
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
@pydantic.model_validator(mode='after')
|
|
367
|
+
@classmethod
|
|
368
|
+
def assign_versions(cls, data):
|
|
369
|
+
if data.app_version == '':
|
|
370
|
+
data.app_version = generate_app_version()
|
|
371
|
+
if data.mmif_version == '':
|
|
372
|
+
data.mmif_version = get_mmif_specver()
|
|
373
|
+
return data
|
|
374
|
+
|
|
375
|
+
@pydantic.field_validator('identifier', mode='before')
|
|
376
|
+
@classmethod
|
|
328
377
|
def append_version(cls, val):
|
|
329
378
|
prefix = f'{app_directory_baseurl if "/" not in val else""}'
|
|
330
379
|
suffix = generate_app_version()
|
|
331
380
|
return '/'.join(map(lambda x: x.strip('/'), filter(None, (prefix, val, suffix))))
|
|
332
381
|
|
|
333
|
-
@pydantic.
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
@pydantic.validator('app_version', pre=True)
|
|
338
|
-
def auto_app_version(cls, val):
|
|
339
|
-
return generate_app_version()
|
|
382
|
+
@pydantic.field_validator('url', 'identifier', mode='after') # because pydantic v2 doesn't auto-convert url to string
|
|
383
|
+
@classmethod
|
|
384
|
+
def stringify(cls, val):
|
|
385
|
+
return str(val)
|
|
340
386
|
|
|
341
387
|
def _check_input_duplicate(self, a_input):
|
|
342
388
|
for elem in self.input:
|
|
@@ -400,9 +446,7 @@ class AppMetadata(pydantic.BaseModel):
|
|
|
400
446
|
:param properties: additional property specifications
|
|
401
447
|
:return: the newly added Output object
|
|
402
448
|
"""
|
|
403
|
-
new = Output(at_type=at_type)
|
|
404
|
-
if len(properties) > 0:
|
|
405
|
-
new.properties = properties
|
|
449
|
+
new = Output(at_type=at_type, properties=properties)
|
|
406
450
|
if new not in self.output:
|
|
407
451
|
self.output.append(new)
|
|
408
452
|
else:
|
|
@@ -412,7 +456,7 @@ class AppMetadata(pydantic.BaseModel):
|
|
|
412
456
|
def add_parameter(self, name: str, description: str, type: param_value_types,
|
|
413
457
|
choices: Optional[List[real_valued_primitives]] = None,
|
|
414
458
|
multivalued: bool = False,
|
|
415
|
-
default: Union[real_valued_primitives, List[real_valued_primitives]] = None):
|
|
459
|
+
default: Union[None, real_valued_primitives, List[real_valued_primitives]] = None):
|
|
416
460
|
"""
|
|
417
461
|
Helper method to add an element to the ``parameters`` list.
|
|
418
462
|
"""
|
|
@@ -456,10 +500,7 @@ class AppMetadata(pydantic.BaseModel):
|
|
|
456
500
|
raise ValueError("Key and value should not be empty!")
|
|
457
501
|
|
|
458
502
|
def jsonify(self, pretty=False):
|
|
459
|
-
if pretty
|
|
460
|
-
return self.json(exclude_defaults=True, by_alias=True, indent=2)
|
|
461
|
-
else:
|
|
462
|
-
return self.json(exclude_defaults=True, by_alias=True)
|
|
503
|
+
return self.model_dump_json(exclude_defaults=True, by_alias=True, indent=2 if pretty else None)
|
|
463
504
|
|
|
464
505
|
|
|
465
506
|
if __name__ == '__main__':
|
|
@@ -3,6 +3,7 @@ The purpose of this file is to define the metadata of the app with minimal impor
|
|
|
3
3
|
|
|
4
4
|
DO NOT CHANGE the name of the file
|
|
5
5
|
"""
|
|
6
|
+
import pathlib
|
|
6
7
|
|
|
7
8
|
from mmif import DocumentTypes, AnnotationTypes
|
|
8
9
|
|
|
@@ -36,7 +37,7 @@ def appmetadata() -> AppMetadata:
|
|
|
36
37
|
# (but, when the app doesn't implementaion any additional algorithms/model/architecture, but simply use API's of existing, for exmaple, OCR software, it is a wrapper)
|
|
37
38
|
# if the analyzer is a python app, and it's specified in the requirements.txt
|
|
38
39
|
# this trick can also be useful (replace ANALYZER_NAME with the pypi dist name)
|
|
39
|
-
analyzer_version=[l.strip().rsplit('==')[-1] for l in open('requirements.txt').readlines() if re.match(r'^ANALYZER_NAME==', l)][0],
|
|
40
|
+
analyzer_version=[l.strip().rsplit('==')[-1] for l in open(pathlib.Path(__file__).parent / 'requirements.txt').readlines() if re.match(r'^ANALYZER_NAME==', l)][0],
|
|
40
41
|
analyzer_license="", # short name for a software license
|
|
41
42
|
)
|
|
42
43
|
# and then add I/O specifications: an app must have at least one input and one output
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.3.2"
|
|
@@ -1,26 +1,37 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: clams-python
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.2
|
|
4
4
|
Summary: A collection of APIs to develop CLAMS app for python
|
|
5
5
|
Home-page: https://clams.ai
|
|
6
6
|
Author: Brandeis Lab for Linguistics and Computation
|
|
7
7
|
Author-email: admin@clams.ai
|
|
8
|
-
|
|
8
|
+
License: Apache-2.0
|
|
9
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
9
10
|
Classifier: Framework :: Flask
|
|
10
11
|
Classifier: Framework :: Pytest
|
|
11
|
-
Classifier: Intended Audience :: Developers
|
|
12
|
-
Classifier: License :: OSI Approved :: Apache Software License
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
13
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
14
|
-
Requires-Python: >=3.
|
|
14
|
+
Requires-Python: >=3.10
|
|
15
15
|
Description-Content-Type: text/markdown
|
|
16
16
|
License-File: LICENSE
|
|
17
|
-
Requires-Dist: mmif-python==1.
|
|
17
|
+
Requires-Dist: mmif-python==1.1.1
|
|
18
18
|
Requires-Dist: Flask>=2
|
|
19
19
|
Requires-Dist: Flask-RESTful>=0.3.9
|
|
20
20
|
Requires-Dist: gunicorn>=20
|
|
21
21
|
Requires-Dist: lapps>=0.0.2
|
|
22
|
-
Requires-Dist: pydantic
|
|
22
|
+
Requires-Dist: pydantic>=2
|
|
23
23
|
Requires-Dist: jsonschema>=3
|
|
24
|
+
Dynamic: author
|
|
25
|
+
Dynamic: author-email
|
|
26
|
+
Dynamic: classifier
|
|
27
|
+
Dynamic: description
|
|
28
|
+
Dynamic: description-content-type
|
|
29
|
+
Dynamic: home-page
|
|
30
|
+
Dynamic: license
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
Dynamic: requires-dist
|
|
33
|
+
Dynamic: requires-python
|
|
34
|
+
Dynamic: summary
|
|
24
35
|
|
|
25
36
|
## CLAMS project
|
|
26
37
|
[CLAMS project](https://clams.ai) aims at free and open-source software platform for computational analysis and metadata generation applications for multimedia material. We believe free and open AI-based multimedia processing platform can bring many benefits of technical advancement to libraries, archives, and museums. To achieve interoperability between various computational applications developed be different vendors, which is absolutely necessary for using applications together supported by user-friendly workflow engines, we are also developing JSON-based MultiMedia Interchange Format ([MMIF](https://mmif.clams.ai))
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
LICENSE
|
|
2
2
|
MANIFEST.in
|
|
3
3
|
README.md
|
|
4
|
+
VERSION
|
|
5
|
+
requirements.txt
|
|
4
6
|
setup.py
|
|
5
|
-
./VERSION
|
|
6
|
-
./requirements.txt
|
|
7
7
|
clams/__init__.py
|
|
8
8
|
clams/app/__init__.py
|
|
9
9
|
clams/appmetadata/__init__.py
|
|
@@ -22,9 +22,6 @@ clams/develop/templates/gha/workflows/issue-apps-project.yml.template
|
|
|
22
22
|
clams/develop/templates/gha/workflows/issue-assign.yml.template
|
|
23
23
|
clams/develop/templates/gha/workflows/issue-close.yml.template
|
|
24
24
|
clams/develop/templates/gha/workflows/publish.yml.template
|
|
25
|
-
clams/mmif_utils/__init__.py
|
|
26
|
-
clams/mmif_utils/rewind.py
|
|
27
|
-
clams/mmif_utils/source.py
|
|
28
25
|
clams/restify/__init__.py
|
|
29
26
|
clams/serve/__init__.py
|
|
30
27
|
clams/ver/__init__.py
|
|
@@ -34,5 +31,4 @@ clams_python.egg-info/dependency_links.txt
|
|
|
34
31
|
clams_python.egg-info/entry_points.txt
|
|
35
32
|
clams_python.egg-info/requires.txt
|
|
36
33
|
clams_python.egg-info/top_level.txt
|
|
37
|
-
tests/test_clamsapp.py
|
|
38
|
-
tests/test_clamscli.py
|
|
34
|
+
tests/test_clamsapp.py
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
#! /usr/bin/env python3
|
|
2
|
-
import distutils.cmd
|
|
3
2
|
import os
|
|
4
3
|
from os import path
|
|
5
4
|
import shutil
|
|
6
5
|
|
|
7
|
-
import setuptools
|
|
8
|
-
|
|
9
6
|
name = "clams-python"
|
|
10
7
|
cmdclass = {}
|
|
11
8
|
|
|
@@ -25,22 +22,7 @@ init_mod = open(path.join(ver_pack_dir, '__init__.py'), 'w')
|
|
|
25
22
|
init_mod.write(f'__version__ = "{version}"')
|
|
26
23
|
init_mod.close()
|
|
27
24
|
|
|
28
|
-
|
|
29
|
-
class DoNothing(distutils.cmd.Command):
|
|
30
|
-
description = "run base code until `setuptools.setup()` line and exits 0."
|
|
31
|
-
user_options = []
|
|
32
|
-
|
|
33
|
-
def initialize_options(self) -> None:
|
|
34
|
-
pass
|
|
35
|
-
|
|
36
|
-
def finalize_options(self) -> None:
|
|
37
|
-
pass
|
|
38
|
-
|
|
39
|
-
def run(self):
|
|
40
|
-
pass
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
cmdclass['donothing'] = DoNothing
|
|
25
|
+
import setuptools
|
|
44
26
|
|
|
45
27
|
setuptools.setup(
|
|
46
28
|
name=name,
|
|
@@ -51,12 +33,12 @@ setuptools.setup(
|
|
|
51
33
|
long_description=long_desc,
|
|
52
34
|
long_description_content_type="text/markdown",
|
|
53
35
|
url="https://clams.ai",
|
|
36
|
+
license="Apache-2.0",
|
|
54
37
|
classifiers=[
|
|
55
|
-
'Development Status ::
|
|
38
|
+
'Development Status :: 5 - Production/Stable',
|
|
56
39
|
'Framework :: Flask',
|
|
57
40
|
'Framework :: Pytest',
|
|
58
|
-
'Intended Audience :: Developers
|
|
59
|
-
'License :: OSI Approved :: Apache Software License',
|
|
41
|
+
'Intended Audience :: Developers',
|
|
60
42
|
'Programming Language :: Python :: 3 :: Only',
|
|
61
43
|
],
|
|
62
44
|
cmdclass=cmdclass,
|
|
@@ -66,7 +48,7 @@ setuptools.setup(
|
|
|
66
48
|
'clams': ['develop/templates/**/*', 'develop/templates/**/.*']
|
|
67
49
|
},
|
|
68
50
|
install_requires=requires,
|
|
69
|
-
python_requires='>=3.
|
|
51
|
+
python_requires='>=3.10',
|
|
70
52
|
packages=setuptools.find_packages(),
|
|
71
53
|
entry_points={
|
|
72
54
|
'console_scripts': [
|
clams_python-1.3.0/VERSION
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
1.3.0
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import sys
|
|
2
|
-
|
|
3
|
-
from mmif import __specver__
|
|
4
|
-
|
|
5
|
-
from clams import develop
|
|
6
|
-
from clams.mmif_utils import source
|
|
7
|
-
from clams.mmif_utils import rewind
|
|
8
|
-
from clams.app import *
|
|
9
|
-
from clams.app import __all__ as app_all
|
|
10
|
-
from clams.appmetadata import AppMetadata
|
|
11
|
-
from clams.restify import Restifier
|
|
12
|
-
from clams.ver import __version__
|
|
13
|
-
|
|
14
|
-
__all__ = [AppMetadata, Restifier] + app_all
|
|
15
|
-
version_template = "{} (based on MMIF spec: {})"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def prep_argparser():
|
|
19
|
-
import argparse
|
|
20
|
-
parser = argparse.ArgumentParser()
|
|
21
|
-
parser.add_argument(
|
|
22
|
-
'-v', '--version',
|
|
23
|
-
action='version',
|
|
24
|
-
version=version_template.format(__version__, __specver__)
|
|
25
|
-
)
|
|
26
|
-
subparsers = parser.add_subparsers(title='sub-command', dest='subcmd')
|
|
27
|
-
for subcmd_module in [source, rewind, develop]:
|
|
28
|
-
subcmd_name = subcmd_module.__name__.rsplit('.')[-1]
|
|
29
|
-
subcmd_parser = subcmd_module.prep_argparser(add_help=False)
|
|
30
|
-
subparsers.add_parser(subcmd_name, parents=[subcmd_parser],
|
|
31
|
-
help=subcmd_module.describe_argparser()[0],
|
|
32
|
-
description=subcmd_module.describe_argparser()[1],
|
|
33
|
-
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
34
|
-
)
|
|
35
|
-
return parser
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def cli():
|
|
39
|
-
parser = prep_argparser()
|
|
40
|
-
if len(sys.argv) == 1:
|
|
41
|
-
parser.print_help(sys.stderr)
|
|
42
|
-
sys.exit(1)
|
|
43
|
-
args = parser.parse_args()
|
|
44
|
-
if args.subcmd == 'source':
|
|
45
|
-
source.main(args)
|
|
46
|
-
if args.subcmd == 'rewind':
|
|
47
|
-
rewind.main(args)
|
|
48
|
-
if args.subcmd == 'develop':
|
|
49
|
-
develop.main(args)
|