Skip to content
This repository was archived by the owner on Aug 25, 2024. It is now read-only.

Commit 36d5359

Browse files
committed
source: json: Use auto args and config
Signed-off-by: John Andersen <[email protected]>
1 parent 5b58db2 commit 36d5359

File tree

7 files changed

+103
-53
lines changed

7 files changed

+103
-53
lines changed

dffml/base.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,11 @@ def config(cls):
110110
"""
111111
Decorator to create a dataclass
112112
"""
113-
return dataclasses.dataclass(eq=True, frozen=True)(cls)
113+
datacls = dataclasses.dataclass(eq=True, frozen=True)(cls)
114+
datacls._replace = lambda self, *args, **kwargs: dataclasses.replace(
115+
self, *args, **kwargs
116+
)
117+
return datacls
114118

115119

116120
class ConfigurableParsingNamespace(object):

dffml/source/file.py

Lines changed: 11 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,38 @@
11
# SPDX-License-Identifier: MIT
22
# Copyright (c) 2019 Intel Corporation
33
import os
4+
import io
45
import abc
5-
import asyncio
6-
import gzip
76
import bz2
7+
import gzip
88
import lzma
9+
import asyncio
910
import zipfile
10-
import io
11-
from typing import NamedTuple, Tuple, Dict
1211
from contextlib import contextmanager
12+
from dataclasses import dataclass, field, fields
13+
from typing import NamedTuple, Tuple, Dict, List
1314

14-
from ..base import BaseConfig
15+
from ..base import config
1516
from .source import BaseSource
1617
from ..util.cli.arg import Arg
1718
from ..util.cli.cmd import CMD
19+
from ..util.entrypoint import entry_point
1820

1921

20-
class FileSourceConfig(BaseConfig, NamedTuple):
22+
@config
23+
class FileSourceConfig:
2124
filename: str
2225
label: str = "unlabeled"
2326
readonly: bool = False
2427

2528

29+
@entry_point("file")
2630
class FileSource(BaseSource):
2731
"""
2832
FileSource reads and write from a file on open / close.
2933
"""
3034

31-
ENTRY_POINT_ORIG_LABEL = "file"
32-
ENTRY_POINT_LABEL = ENTRY_POINT_ORIG_LABEL
35+
CONFIG = FileSourceConfig
3336

3437
async def __aenter__(self) -> "BaseSourceContext":
3538
await self._open()
@@ -108,25 +111,3 @@ async def load_fd(self, fd):
108111
@abc.abstractmethod
109112
async def dump_fd(self, fd):
110113
pass # pragma: no cover
111-
112-
@classmethod
113-
def args(cls, args, *above) -> Dict[str, Arg]:
114-
cls.config_set(args, above, "filename", Arg())
115-
cls.config_set(
116-
args,
117-
above,
118-
"readonly",
119-
Arg(type=bool, action="store_true", default=False),
120-
)
121-
cls.config_set(
122-
args, above, "label", Arg(type=str, default="unlabeled")
123-
)
124-
return args
125-
126-
@classmethod
127-
def config(cls, config, *above):
128-
return FileSourceConfig(
129-
filename=cls.config_get(config, above, "filename"),
130-
readonly=cls.config_get(config, above, "readonly"),
131-
label=cls.config_get(config, above, "label"),
132-
)

dffml/source/json.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,18 @@
88

99
from ..repo import Repo
1010
from .memory import MemorySource
11-
from .file import FileSource
11+
from .file import FileSource, FileSourceConfig
1212
from ..util.entrypoint import entry_point
1313

1414
from .log import LOGGER
1515

1616
LOGGER = LOGGER.getChild("json")
1717

1818

19+
class JSONSourceConfig(FileSourceConfig):
20+
pass # pragma: no cov
21+
22+
1923
@dataclass
2024
class OpenJSONFile:
2125
data: Dict[str, Dict]
@@ -39,6 +43,7 @@ class JSONSource(FileSource, MemorySource):
3943
stored in memory.
4044
"""
4145

46+
CONFIG = JSONSourceConfig
4247
OPEN_JSON_FILES: Dict[str, OpenJSONFile] = {}
4348
OPEN_JSON_FILES_LOCK: asyncio.Lock = asyncio.Lock()
4449

dffml/source/memory.py

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import asyncio
77
from typing import Any, Dict, List, NamedTuple, AsyncIterator
88

9-
from ..base import BaseConfig
9+
from ..base import config
1010
from ..repo import Repo
1111
from .source import BaseSourceContext, BaseSource
1212
from ..util.cli.arg import Arg
@@ -25,7 +25,8 @@ async def repo(self, src_url: str) -> Repo:
2525
return self.parent.mem.get(src_url, Repo(src_url))
2626

2727

28-
class MemorySourceConfig(BaseConfig, NamedTuple):
28+
@config
29+
class MemorySourceConfig:
2930
repos: List[Repo]
3031

3132

@@ -35,23 +36,11 @@ class MemorySource(BaseSource):
3536
Stores repos in a dict in memory
3637
"""
3738

39+
CONFIG = MemorySourceConfig
3840
CONTEXT = MemorySourceContext
3941

40-
def __init__(self, config: BaseConfig) -> None:
42+
def __init__(self, config: MemorySourceConfig) -> None:
4143
super().__init__(config)
4244
self.mem: Dict[str, Repo] = {}
4345
if isinstance(self.config, MemorySourceConfig):
4446
self.mem = {repo.src_url: repo for repo in self.config.repos}
45-
46-
@classmethod
47-
def args(cls, args, *above) -> Dict[str, Arg]:
48-
cls.config_set(
49-
args, above, "keys", Arg(type=str, nargs="+", default=[])
50-
)
51-
return args
52-
53-
@classmethod
54-
def config(cls, config, *above):
55-
return MemorySourceConfig(
56-
repos=list(map(Repo, cls.config_get(config, above, "keys")))
57-
)

tests/source/test_file.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,10 @@ def test_args(self):
6161
"file": {
6262
"arg": None,
6363
"config": {
64-
"filename": {"arg": Arg(), "config": {}},
64+
"filename": {
65+
"arg": Arg(type=str),
66+
"config": {},
67+
},
6568
"readonly": {
6669
"arg": Arg(
6770
type=bool,

tests/source/test_json.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
# SPDX-License-Identifier: MIT
22
# Copyright (c) 2019 Intel Corporation
3-
from dffml.source.file import FileSourceConfig
4-
from dffml.source.json import JSONSource
3+
from dffml.source.json import JSONSource, JSONSourceConfig
54
from dffml.util.testing.source import FileSourceTest
65
from dffml.util.asynctestcase import AsyncTestCase
76

87

9-
class TestCustomSQliteSource(FileSourceTest, AsyncTestCase):
8+
class TestJSONSource(FileSourceTest, AsyncTestCase):
109
async def setUpSource(self):
11-
return JSONSource(FileSourceConfig(filename=self.testfile))
10+
return JSONSource(JSONSourceConfig(filename=self.testfile))

tests/test_integration_cli.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
"""
2+
This file contains integration tests. We use the CLI to exercise functionality of
3+
various DFFML classes and constructs.
4+
"""
5+
import io
6+
import contextlib
7+
8+
from dffml.cli.cli import CLI
9+
from dffml.util.asynctestcase import AsyncTestCase
10+
11+
from .test_cli import non_existant_tempfile
12+
13+
14+
class IntegrationCLITestCase(AsyncTestCase):
15+
async def setUp(self):
16+
super().setUp()
17+
self.stdout = io.StringIO()
18+
self._stack = contextlib.ExitStack().__enter__()
19+
20+
async def tearDown(self):
21+
super().tearDown()
22+
self._stack.__exit__(None, None, None)
23+
24+
def mktempfile(self):
25+
return self._stack.enter_context(non_existant_tempfile())
26+
27+
28+
class TestList(IntegrationCLITestCase):
29+
async def test_repos(self):
30+
src_urls = ["A", "B", "C"]
31+
with contextlib.redirect_stdout(self.stdout):
32+
await CLI.cli(
33+
"list",
34+
"repos",
35+
"-sources",
36+
"feed=memory",
37+
"-source-repos",
38+
*src_urls,
39+
)
40+
stdout = self.stdout.getvalue()
41+
for src_url in src_urls:
42+
self.assertIn(src_url, stdout)
43+
44+
45+
class TestMerge(IntegrationCLITestCase):
46+
async def test_memory_to_json(self):
47+
src_urls = ["A", "B", "C"]
48+
filename = self.mktempfile()
49+
await CLI.cli(
50+
"merge",
51+
"dest=json",
52+
"src=memory",
53+
"-source-dest-filename",
54+
filename,
55+
"-source-src-repos",
56+
*src_urls,
57+
)
58+
with contextlib.redirect_stdout(self.stdout):
59+
await CLI.cli(
60+
"list",
61+
"repos",
62+
"-sources",
63+
"tmp=json",
64+
"-source-tmp-filename",
65+
filename,
66+
)
67+
stdout = self.stdout.getvalue()
68+
for src_url in src_urls:
69+
self.assertIn(src_url, stdout)

0 commit comments

Comments
 (0)