datatailr 0.1.1__py3-none-any.whl → 0.1.2__py3-none-any.whl
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.
Potentially problematic release.
This version of datatailr might be problematic. Click here for more details.
- datatailr/__init__.py +63 -0
- datatailr/acl.py +80 -0
- datatailr/blob.py +103 -0
- datatailr/build/__init__.py +11 -0
- datatailr/build/image.py +87 -0
- datatailr/dt_json.py +42 -0
- datatailr/errors.py +10 -0
- datatailr/group.py +136 -0
- datatailr/logging.py +93 -0
- datatailr/sbin/run_job.py +63 -0
- datatailr/scheduler/__init__.py +38 -0
- datatailr/scheduler/arguments_cache.py +126 -0
- datatailr/scheduler/base.py +238 -0
- datatailr/scheduler/batch.py +347 -0
- datatailr/scheduler/batch_decorator.py +112 -0
- datatailr/scheduler/constants.py +20 -0
- datatailr/scheduler/utils.py +28 -0
- datatailr/user.py +201 -0
- datatailr/utils.py +35 -0
- datatailr/version.py +14 -0
- datatailr/wrapper.py +204 -0
- {datatailr-0.1.1.dist-info → datatailr-0.1.2.dist-info}/METADATA +8 -3
- datatailr-0.1.2.dist-info/RECORD +29 -0
- {datatailr-0.1.1.dist-info → datatailr-0.1.2.dist-info}/WHEEL +1 -1
- datatailr-0.1.2.dist-info/entry_points.txt +2 -0
- datatailr-0.1.2.dist-info/top_level.txt +2 -0
- test_module/__init__.py +17 -0
- test_module/test_submodule.py +38 -0
- datatailr-0.1.1.dist-info/RECORD +0 -6
- datatailr-0.1.1.dist-info/top_level.txt +0 -1
- {datatailr-0.1.1.dist-info → datatailr-0.1.2.dist-info}/licenses/LICENSE +0 -0
datatailr/user.py
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# *************************************************************************
|
|
2
|
+
#
|
|
3
|
+
# Copyright (c) 2025 - Datatailr Inc.
|
|
4
|
+
# All Rights Reserved.
|
|
5
|
+
#
|
|
6
|
+
# This file is part of Datatailr and subject to the terms and conditions
|
|
7
|
+
# defined in 'LICENSE.txt'. Unauthorized copying and/or distribution
|
|
8
|
+
# of this file, in parts or full, via any medium is strictly prohibited.
|
|
9
|
+
# *************************************************************************
|
|
10
|
+
|
|
11
|
+
from typing import Optional
|
|
12
|
+
|
|
13
|
+
from datatailr import dt__User
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class User:
|
|
17
|
+
"""Representing a Datatailr User
|
|
18
|
+
This class provides methods to interact with the Datatailr User API.
|
|
19
|
+
It allows you to create, update, delete, and manage users within the Datatailr platform.
|
|
20
|
+
Attributes:
|
|
21
|
+
first_name (str): The first name of the user.
|
|
22
|
+
last_name (str): The last name of the user.
|
|
23
|
+
name (str): The username of the user.
|
|
24
|
+
email (str): The email address of the user.
|
|
25
|
+
user_id (int): The unique identifier for the user.
|
|
26
|
+
primary_group_id (int): The primary group of the user.
|
|
27
|
+
is_system_user (bool): Indicates if the user is a system user.
|
|
28
|
+
Static Methods:
|
|
29
|
+
signed_user() -> Optional['User']:
|
|
30
|
+
Retrieve the currently signed-in user, if available.
|
|
31
|
+
add(name: str, first_name: str = None, last_name: str = None, email: str = None, password: str = None, primary_group_id: int = None, is_system_user: bool = False) -> 'User':
|
|
32
|
+
Create a new user with the specified username, first name, last name, and email.
|
|
33
|
+
get(name: str) -> 'User':
|
|
34
|
+
Retrieve a user by their username.
|
|
35
|
+
exists(name: str) -> bool:
|
|
36
|
+
Check if a user exists by their username.
|
|
37
|
+
ls() -> list:
|
|
38
|
+
List all users available in the Datatailr platform.
|
|
39
|
+
remove(name: str) -> None:
|
|
40
|
+
Remove a user by their username.
|
|
41
|
+
Instance Methods:
|
|
42
|
+
verify() -> None:
|
|
43
|
+
Refresh the user information from the Datatailr API.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
# Datatailr User API Client
|
|
47
|
+
__client__ = dt__User()
|
|
48
|
+
|
|
49
|
+
def __init__(self, name):
|
|
50
|
+
self.__name = name
|
|
51
|
+
self.__first_name = None
|
|
52
|
+
self.__last_name = None
|
|
53
|
+
self.__email = None
|
|
54
|
+
self.__user_id = None
|
|
55
|
+
self.__primary_group_id = None
|
|
56
|
+
self.__is_system_user = False
|
|
57
|
+
self.__expiry__ = None
|
|
58
|
+
self.__signature__ = None
|
|
59
|
+
|
|
60
|
+
self.__refresh__()
|
|
61
|
+
|
|
62
|
+
def __repr__(self):
|
|
63
|
+
return (
|
|
64
|
+
f"User(name={self.name}, first_name={self.first_name}, "
|
|
65
|
+
f"last_name={self.last_name}, email={self.email}, "
|
|
66
|
+
f"user_id={self.user_id}, primary_group_id={self.primary_group_id}, "
|
|
67
|
+
f"is_system_user={self.is_system_user})"
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
def __str__(self):
|
|
71
|
+
return f"<User: {self.name} | {self.user_id}>"
|
|
72
|
+
|
|
73
|
+
def __eq__(self, other):
|
|
74
|
+
if not isinstance(other, User):
|
|
75
|
+
return NotImplemented
|
|
76
|
+
return (
|
|
77
|
+
self.user_id == other.user_id
|
|
78
|
+
and self.name == other.name
|
|
79
|
+
and self.email == other.email
|
|
80
|
+
and self.primary_group_id == other.primary_group_id
|
|
81
|
+
and self.is_system_user == other.is_system_user
|
|
82
|
+
and self.first_name == other.first_name
|
|
83
|
+
and self.last_name == other.last_name
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
def __refresh__(self):
|
|
87
|
+
if not self.name:
|
|
88
|
+
raise ValueError("Name is not set. Cannot refresh user.")
|
|
89
|
+
user = self.__client__.get(self.name)
|
|
90
|
+
if user:
|
|
91
|
+
self.__name = user["name"]
|
|
92
|
+
self.__first_name = user["first_name"]
|
|
93
|
+
self.__last_name = user["last_name"]
|
|
94
|
+
self.__email = user.get("email")
|
|
95
|
+
self.__user_id = user["user_id"]
|
|
96
|
+
self.__primary_group_id = user["primary_group_id"]
|
|
97
|
+
self.__is_system_user = user["is_system"]
|
|
98
|
+
|
|
99
|
+
@property
|
|
100
|
+
def first_name(self):
|
|
101
|
+
return self.__first_name
|
|
102
|
+
|
|
103
|
+
@property
|
|
104
|
+
def last_name(self):
|
|
105
|
+
return self.__last_name
|
|
106
|
+
|
|
107
|
+
@property
|
|
108
|
+
def name(self):
|
|
109
|
+
return self.__name
|
|
110
|
+
|
|
111
|
+
@property
|
|
112
|
+
def email(self):
|
|
113
|
+
return self.__email
|
|
114
|
+
|
|
115
|
+
@property
|
|
116
|
+
def user_id(self):
|
|
117
|
+
return self.__user_id
|
|
118
|
+
|
|
119
|
+
@property
|
|
120
|
+
def primary_group_id(self):
|
|
121
|
+
return self.__primary_group_id
|
|
122
|
+
|
|
123
|
+
@property
|
|
124
|
+
def primary_group(self):
|
|
125
|
+
if self.__primary_group_id is not None:
|
|
126
|
+
from datatailr.group import Group
|
|
127
|
+
|
|
128
|
+
return Group.get(self.__primary_group_id)
|
|
129
|
+
return None
|
|
130
|
+
|
|
131
|
+
@property
|
|
132
|
+
def is_system_user(self):
|
|
133
|
+
return self.__is_system_user
|
|
134
|
+
|
|
135
|
+
@staticmethod
|
|
136
|
+
def get(name: str) -> Optional["User"]:
|
|
137
|
+
return User(name)
|
|
138
|
+
|
|
139
|
+
@staticmethod
|
|
140
|
+
def signed_user() -> "User":
|
|
141
|
+
user_signature_and_expiry = User.__client__.signed_user()
|
|
142
|
+
if user_signature_and_expiry:
|
|
143
|
+
user = User(name=user_signature_and_expiry["name"])
|
|
144
|
+
user.__expiry__ = user_signature_and_expiry["expiry"]
|
|
145
|
+
user.__signature__ = user_signature_and_expiry["signature"]
|
|
146
|
+
return user
|
|
147
|
+
raise PermissionError(
|
|
148
|
+
"No signed user found. Please ensure you are signed in to Datatailr."
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
@staticmethod
|
|
152
|
+
def add(
|
|
153
|
+
name: str,
|
|
154
|
+
first_name: str,
|
|
155
|
+
last_name: str,
|
|
156
|
+
email: str,
|
|
157
|
+
password: str,
|
|
158
|
+
primary_group: int,
|
|
159
|
+
is_system_user: bool = False,
|
|
160
|
+
) -> Optional["User"]:
|
|
161
|
+
if is_system_user:
|
|
162
|
+
if password is not None:
|
|
163
|
+
raise Warning(
|
|
164
|
+
"Password is not required for system users. It will be ignored."
|
|
165
|
+
)
|
|
166
|
+
User.__client__.add(
|
|
167
|
+
name,
|
|
168
|
+
first_name=first_name,
|
|
169
|
+
last_name=last_name,
|
|
170
|
+
email=email,
|
|
171
|
+
primary_group=primary_group,
|
|
172
|
+
system=is_system_user,
|
|
173
|
+
)
|
|
174
|
+
else:
|
|
175
|
+
User.__client__.add(
|
|
176
|
+
name,
|
|
177
|
+
first_name=first_name,
|
|
178
|
+
last_name=last_name,
|
|
179
|
+
email=email,
|
|
180
|
+
password=password,
|
|
181
|
+
primary_group=primary_group,
|
|
182
|
+
system=is_system_user,
|
|
183
|
+
)
|
|
184
|
+
return User.get(name)
|
|
185
|
+
|
|
186
|
+
@staticmethod
|
|
187
|
+
def exists(name: str) -> bool:
|
|
188
|
+
return User.__client__.exists(name)
|
|
189
|
+
|
|
190
|
+
@staticmethod
|
|
191
|
+
def ls() -> list:
|
|
192
|
+
users = User.__client__.ls()
|
|
193
|
+
return [User.get(user["name"]) for user in users]
|
|
194
|
+
|
|
195
|
+
@staticmethod
|
|
196
|
+
def remove(name: str) -> None:
|
|
197
|
+
User.__client__.rm(name)
|
|
198
|
+
return None
|
|
199
|
+
|
|
200
|
+
def verify(self) -> None:
|
|
201
|
+
return self.__client__.verify(self.name, self.__expiry__, self.__signature__)
|
datatailr/utils.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# *************************************************************************
|
|
2
|
+
#
|
|
3
|
+
# Copyright (c) 2025 - Datatailr Inc.
|
|
4
|
+
# All Rights Reserved.
|
|
5
|
+
#
|
|
6
|
+
# This file is part of Datatailr and subject to the terms and conditions
|
|
7
|
+
# defined in 'LICENSE.txt'. Unauthorized copying and/or distribution
|
|
8
|
+
# of this file, in parts or full, via any medium is strictly prohibited.
|
|
9
|
+
# *************************************************************************
|
|
10
|
+
|
|
11
|
+
import shutil
|
|
12
|
+
from enum import Enum
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Environment(Enum):
|
|
16
|
+
"""
|
|
17
|
+
Enum representing different environments for DataTailr jobs.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
DEV = "dev"
|
|
21
|
+
PRE = "pre"
|
|
22
|
+
PROD = "prod"
|
|
23
|
+
|
|
24
|
+
def __str__(self):
|
|
25
|
+
return self.value
|
|
26
|
+
|
|
27
|
+
def __repr__(self):
|
|
28
|
+
return f"Environment.{self.name}('{self.value}')"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def is_dt_installed():
|
|
32
|
+
"""
|
|
33
|
+
Check if DataTailr is installed by looking for the 'dt' command in the system PATH.
|
|
34
|
+
"""
|
|
35
|
+
return shutil.which("dt") is not None
|
datatailr/version.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import importlib.metadata
|
|
2
|
+
|
|
3
|
+
try:
|
|
4
|
+
__version__ = importlib.metadata.version("datatailr")
|
|
5
|
+
except importlib.metadata.PackageNotFoundError:
|
|
6
|
+
import toml # type: ignore
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
# load the version from pyproject.toml which is located two levels up relative to this file
|
|
10
|
+
this_file_path = __file__
|
|
11
|
+
pyproject_path = this_file_path.rsplit("/", 3)[0] + "/pyproject.toml"
|
|
12
|
+
__version__ = toml.load(pyproject_path)["project"]["version"]
|
|
13
|
+
except (FileNotFoundError, KeyError):
|
|
14
|
+
__version__ = "unknown"
|
datatailr/wrapper.py
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# *************************************************************************
|
|
2
|
+
#
|
|
3
|
+
# Copyright (c) 2025 - Datatailr Inc.
|
|
4
|
+
# All Rights Reserved.
|
|
5
|
+
#
|
|
6
|
+
# This file is part of Datatailr and subject to the terms and conditions
|
|
7
|
+
# defined in 'LICENSE.txt'. Unauthorized copying and/or distribution
|
|
8
|
+
# of this file, in parts or full, via any medium is strictly prohibited.
|
|
9
|
+
# *************************************************************************
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import os
|
|
13
|
+
import subprocess
|
|
14
|
+
|
|
15
|
+
from datatailr.utils import is_dt_installed
|
|
16
|
+
|
|
17
|
+
if is_dt_installed() or os.path.exists("/opt/datatailr/etc/api.json"):
|
|
18
|
+
API_JSON_PATH = os.path.join("/opt", "datatailr", "etc", "api.json")
|
|
19
|
+
CLI_TOOL = "dt"
|
|
20
|
+
else:
|
|
21
|
+
# For running local tests, use api.json from the repo and a mock CLI tool
|
|
22
|
+
API_JSON_PATH = os.path.join(
|
|
23
|
+
os.path.dirname(__file__), "..", "..", "..", "..", "dt", "api.json"
|
|
24
|
+
)
|
|
25
|
+
CLI_TOOL = "echo"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def type_map(arg_type):
|
|
29
|
+
mapping = {
|
|
30
|
+
"String": "str",
|
|
31
|
+
"file": "str",
|
|
32
|
+
"boolean": "bool",
|
|
33
|
+
"int": "int",
|
|
34
|
+
"float": "float",
|
|
35
|
+
}
|
|
36
|
+
return mapping.get(arg_type, "str")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def add_quotes(arg):
|
|
40
|
+
"""Add quotes to the argument if it contains spaces or special characters."""
|
|
41
|
+
if isinstance(arg, str) and (" " in arg or '"' in arg or "'" in arg):
|
|
42
|
+
return f'"{arg}"'
|
|
43
|
+
return str(arg)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def make_method(cmd_name, sub_name, sub_def):
|
|
47
|
+
arg_names = sub_def.get("non_options", {}).get("arg_names", [])
|
|
48
|
+
arg_types = sub_def.get("non_options", {}).get("arg_types", [])
|
|
49
|
+
min_args = sub_def.get("non_options", {}).get("min", len(arg_names))
|
|
50
|
+
max_args = sub_def.get("non_options", {}).get("max", len(arg_names))
|
|
51
|
+
options = sub_def.get("options", [])
|
|
52
|
+
option_names = [opt[1] for opt in options]
|
|
53
|
+
description = sub_def.get("description", "")
|
|
54
|
+
help_text = sub_def.get("help", "")
|
|
55
|
+
return_type = "str"
|
|
56
|
+
|
|
57
|
+
# Build docstring
|
|
58
|
+
doc = f"""{description}\n\n{help_text}\n"""
|
|
59
|
+
if arg_names:
|
|
60
|
+
doc += "\nArgs:\n"
|
|
61
|
+
for n, t in zip(arg_names, arg_types):
|
|
62
|
+
doc += f" {n} ({type_map(t)}):\n"
|
|
63
|
+
if options:
|
|
64
|
+
doc += "\nOptions:\n"
|
|
65
|
+
for opt in options:
|
|
66
|
+
short, long, opt_type = opt[:3]
|
|
67
|
+
doc += f" --{long} ({opt_type})\n"
|
|
68
|
+
doc += f"\nReturns:\n {return_type}\n"
|
|
69
|
+
|
|
70
|
+
def method(self, *args, **kwargs):
|
|
71
|
+
# Accept required args as either positional or keyword arguments
|
|
72
|
+
all_args = list(args)
|
|
73
|
+
used_kwargs = set()
|
|
74
|
+
kwargs = {k.replace("_", "-"): v for k, v in kwargs.items()}
|
|
75
|
+
# Fill missing positional args from kwargs if available
|
|
76
|
+
for i in range(len(all_args), len(arg_names)):
|
|
77
|
+
arg_name = arg_names[i]
|
|
78
|
+
if arg_name in kwargs:
|
|
79
|
+
all_args.append(kwargs[arg_name])
|
|
80
|
+
used_kwargs.add(arg_name)
|
|
81
|
+
# Argument count check
|
|
82
|
+
missing = []
|
|
83
|
+
if len(all_args) < min_args:
|
|
84
|
+
for i in range(len(all_args), min_args):
|
|
85
|
+
if i < len(arg_names):
|
|
86
|
+
missing.append(
|
|
87
|
+
f"{arg_names[i]}: {type_map(arg_types[i]) if i < len(arg_types) else 'str'}"
|
|
88
|
+
)
|
|
89
|
+
if not (min_args <= len(all_args) <= max_args):
|
|
90
|
+
msg = f"{cmd_name}{' ' + sub_name if sub_name else ''} expects between {min_args} and {max_args} arguments, got {len(all_args)}."
|
|
91
|
+
if missing:
|
|
92
|
+
msg += f" Missing: {', '.join(missing)}"
|
|
93
|
+
raise TypeError(msg)
|
|
94
|
+
# Argument type check
|
|
95
|
+
for i, (arg, expected_type) in enumerate(zip(all_args, arg_types)):
|
|
96
|
+
py_type = eval(type_map(expected_type))
|
|
97
|
+
if not isinstance(arg, py_type):
|
|
98
|
+
raise TypeError(
|
|
99
|
+
f"Argument '{arg_names[i]}' must be of type {py_type.__name__}, got {type(arg).__name__}"
|
|
100
|
+
)
|
|
101
|
+
# Disallow unexpected kwargs (only allow options and used arg-names)
|
|
102
|
+
allowed_kwargs = set(option_names) | set(arg_names)
|
|
103
|
+
unexpected_kwargs = set(kwargs.keys()) - allowed_kwargs
|
|
104
|
+
if unexpected_kwargs:
|
|
105
|
+
raise TypeError(
|
|
106
|
+
f"{cmd_name}{' ' + sub_name if sub_name else ''} got unexpected keyword arguments: {', '.join(unexpected_kwargs)}. Expected: {', '.join(allowed_kwargs)}"
|
|
107
|
+
)
|
|
108
|
+
cmd = [self.cli_tool, cmd_name]
|
|
109
|
+
if sub_name:
|
|
110
|
+
cmd.append(sub_name)
|
|
111
|
+
for arg in all_args:
|
|
112
|
+
cmd.append(add_quotes(str(arg)))
|
|
113
|
+
# Only pass option kwargs (not those used for required args)
|
|
114
|
+
if "json" in allowed_kwargs:
|
|
115
|
+
kwargs["json"] = True # Ensure JSON output if 'json' is an option
|
|
116
|
+
for k, v in kwargs.items():
|
|
117
|
+
if k in option_names:
|
|
118
|
+
# Find the option definition
|
|
119
|
+
opt_def = next((opt for opt in options if opt[1] == k), None)
|
|
120
|
+
if opt_def and opt_def[2] == "no_argument":
|
|
121
|
+
if v:
|
|
122
|
+
cmd.append(f"--{k}")
|
|
123
|
+
# If v is False, omit it (do nothing)
|
|
124
|
+
else:
|
|
125
|
+
# For options that require a value
|
|
126
|
+
cmd.append(f"--{k}={v}")
|
|
127
|
+
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
128
|
+
if result.returncode != 0:
|
|
129
|
+
raise RuntimeError(f"Command failed: {' '.join(cmd)}\n{result.stderr}")
|
|
130
|
+
try:
|
|
131
|
+
return json.loads(result.stdout) if result.stdout else None
|
|
132
|
+
except json.JSONDecodeError:
|
|
133
|
+
return result.stdout.strip()
|
|
134
|
+
|
|
135
|
+
method.__doc__ = doc
|
|
136
|
+
return method
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def create_class(cmd_name, command):
|
|
140
|
+
class_name = cmd_name.capitalize()
|
|
141
|
+
sub_commands = command.get("sub_commands", {})
|
|
142
|
+
# Create a new class with only cli_tool attribute
|
|
143
|
+
cls = type(class_name, (object,), {"cli_tool": CLI_TOOL})
|
|
144
|
+
if sub_commands:
|
|
145
|
+
for sub_name, sub_def in sub_commands.items():
|
|
146
|
+
sub_name = (
|
|
147
|
+
sub_name # Convert hyphens to underscores for Python method names
|
|
148
|
+
)
|
|
149
|
+
method = make_method(cmd_name, sub_name, sub_def)
|
|
150
|
+
setattr(cls, sub_name.replace("-", "_"), method)
|
|
151
|
+
else:
|
|
152
|
+
method = make_method(cmd_name, None, command)
|
|
153
|
+
setattr(cls, cmd_name, method)
|
|
154
|
+
return cls
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
# Load API JSON and create classes at import time
|
|
158
|
+
if os.path.exists(API_JSON_PATH):
|
|
159
|
+
with open(API_JSON_PATH, "r") as f:
|
|
160
|
+
api = json.load(f)
|
|
161
|
+
|
|
162
|
+
for cmd_name, command in api.items():
|
|
163
|
+
cls = create_class(cmd_name, command)
|
|
164
|
+
globals()["dt__" + cmd_name.capitalize()] = cls
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
class mock_cli_tool:
|
|
168
|
+
"""
|
|
169
|
+
Mock CLI tool for local usage of the DataTailr platform.
|
|
170
|
+
This function simulates the CLI tool behavior by returning a predefined response.
|
|
171
|
+
"""
|
|
172
|
+
|
|
173
|
+
# make it possible to use any function, even if it doesn't eist in the class
|
|
174
|
+
def mock_method(self, name, *args, **kwargs):
|
|
175
|
+
def fun(*args, **kwargs):
|
|
176
|
+
return {"mocked": name, "args": args, "kwargs": kwargs}
|
|
177
|
+
|
|
178
|
+
return fun
|
|
179
|
+
|
|
180
|
+
def __getattr__(self, name):
|
|
181
|
+
return self.mock_method(name)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
dt__User = globals().get("dt__User", mock_cli_tool)
|
|
185
|
+
dt__Group = globals().get("dt__Group", mock_cli_tool)
|
|
186
|
+
dt__Job = globals().get("dt__Job", mock_cli_tool)
|
|
187
|
+
dt__Blob = globals().get("dt__Blob", mock_cli_tool)
|
|
188
|
+
dt__Dns = globals().get("dt__Dns", mock_cli_tool)
|
|
189
|
+
dt__System = globals().get("dt__System", mock_cli_tool)
|
|
190
|
+
dt__Sms = globals().get("dt__Sms", mock_cli_tool)
|
|
191
|
+
dt__Group = globals().get("dt__Group", mock_cli_tool)
|
|
192
|
+
dt__Job = globals().get("dt__Job", mock_cli_tool)
|
|
193
|
+
dt__Blob = globals().get("dt__Blob", mock_cli_tool)
|
|
194
|
+
dt__Dns = globals().get("dt__Dns", mock_cli_tool)
|
|
195
|
+
dt__System = globals().get("dt__System", mock_cli_tool)
|
|
196
|
+
dt__Sms = globals().get("dt__Sms", mock_cli_tool)
|
|
197
|
+
dt__Email = globals().get("dt__Email", mock_cli_tool)
|
|
198
|
+
dt__Kv = globals().get("dt__Kv", mock_cli_tool)
|
|
199
|
+
dt__Log = globals().get("dt__Log", mock_cli_tool)
|
|
200
|
+
dt__Node = globals().get("dt__Node", mock_cli_tool)
|
|
201
|
+
dt__Tag = globals().get("dt__Tag", mock_cli_tool)
|
|
202
|
+
dt__Registry = globals().get("dt__Registry", mock_cli_tool)
|
|
203
|
+
dt__Service = globals().get("dt__Service", mock_cli_tool)
|
|
204
|
+
dt__Settings = globals().get("dt__Settings", mock_cli_tool)
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: datatailr
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: The datatailr package
|
|
5
5
|
Author-email: Datatailr <pypi@datatailr.com>
|
|
6
|
-
License: MIT
|
|
6
|
+
License-Expression: MIT
|
|
7
7
|
Classifier: Programming Language :: Python :: 3
|
|
8
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
9
8
|
Classifier: Operating System :: OS Independent
|
|
10
9
|
Requires-Python: >=3.7
|
|
11
10
|
Description-Content-Type: text/markdown
|
|
12
11
|
License-File: LICENSE
|
|
12
|
+
Provides-Extra: dev
|
|
13
|
+
Requires-Dist: ruff; extra == "dev"
|
|
14
|
+
Requires-Dist: pre-commit; extra == "dev"
|
|
15
|
+
Requires-Dist: mypy; extra == "dev"
|
|
16
|
+
Requires-Dist: types-setuptools; extra == "dev"
|
|
17
|
+
Requires-Dist: toml; extra == "dev"
|
|
13
18
|
Dynamic: license-file
|
|
14
19
|
|
|
15
20
|
# datatailr
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
datatailr/__init__.py,sha256=Z_lrDoLOnfiVlVcp5a-jCmjCNsA3Ypne0Lkv6tOTxjA,1390
|
|
2
|
+
datatailr/acl.py,sha256=gLuzw6FLFYLVoyXOS_oHMfd5uGKKzlDqR1WBdN_AGwQ,2862
|
|
3
|
+
datatailr/blob.py,sha256=VImsbKBaYP6oG2Wiy4oPr6zxtUibEHpfI8p8nE1wj14,3195
|
|
4
|
+
datatailr/dt_json.py,sha256=oos6hwC4UxT4TEbbXNSozQGhwnc_bfpo-Y2rj24ks9A,1422
|
|
5
|
+
datatailr/errors.py,sha256=0CHvBOlzvDWoePk_preMy2qKlsztKCuQNdaQDbhNyaU,204
|
|
6
|
+
datatailr/group.py,sha256=-zDktOVhbGfmNn-3eMqDSyL6Sr2uz0gkHmCHtMrHMnA,4391
|
|
7
|
+
datatailr/logging.py,sha256=b3Uaumdo1ZZOaTnD-7iH1rlieWsllQSijNn9rX4svuo,3053
|
|
8
|
+
datatailr/user.py,sha256=2N9HzeHixwZ_Hsu0VM7YYbyJCoemGW9eASzT1rriycQ,6615
|
|
9
|
+
datatailr/utils.py,sha256=eHXOc7VwIUR8ryn5jBmw5QI00ARERJwXV5oICdVRnmQ,937
|
|
10
|
+
datatailr/version.py,sha256=N9K8ZxlwFFSz8XSgbgaTWZY4k2J0JKfj698nZ_O2pIU,536
|
|
11
|
+
datatailr/wrapper.py,sha256=Sm6OruuwOKx2tA01ftFEvp4Hfl9yWY_BVXwHbWCTRzE,8141
|
|
12
|
+
datatailr/build/__init__.py,sha256=_dA7b4L6wsaAFaSxUoYSJ1oaRqDHDMR20kqoCocSOss,487
|
|
13
|
+
datatailr/build/image.py,sha256=P2MiGxpzuZ6hOm9JubkLoOq-RdzKYnXbKJHiryIbJeA,3103
|
|
14
|
+
datatailr/sbin/run_job.py,sha256=B3H3UI3zpll7zVm7lZLsfPtnRlA6jjk8s_D_w89pvC4,2175
|
|
15
|
+
datatailr/scheduler/__init__.py,sha256=YtCnv9vuX-EPGr3WBXvXJIlLsZ94mW1dBzI6h7Yjcu0,1009
|
|
16
|
+
datatailr/scheduler/arguments_cache.py,sha256=-JnAWXfHMSSkYJx8iYt9JgvdPgR-1Z8DcN5Kvcx9Amo,4574
|
|
17
|
+
datatailr/scheduler/base.py,sha256=trsW3ETK0X00qoYYRHQrkfxoHEWhwgTrsJFVrhu6saY,7623
|
|
18
|
+
datatailr/scheduler/batch.py,sha256=daxFRl5894Hb3cKEm_pubXchNwN8qjVuFi0G-zMj-kI,10712
|
|
19
|
+
datatailr/scheduler/batch_decorator.py,sha256=o6DAEODwsfYx9uYwjnaJZ9iUmqbFH2wMh-jz_L5bgNo,4598
|
|
20
|
+
datatailr/scheduler/constants.py,sha256=ISG5uMnVPbGbjaaulU0xdmSggnd-DMr9ed0WTAZSUmU,604
|
|
21
|
+
datatailr/scheduler/utils.py,sha256=YHtAc5sCwfgiClr5G6R3hfAjdlrFdnNW2l-3XwPZLXM,1070
|
|
22
|
+
datatailr-0.1.2.dist-info/licenses/LICENSE,sha256=ikKP4_O-UD_b8FuNdKmbzTb6odd0JX085ZW_FAPN3VI,1066
|
|
23
|
+
test_module/__init__.py,sha256=OF9XaL3RKdbWD5Ug4L-ufLqbykSt_rTK6gqZr4uBJ8g,576
|
|
24
|
+
test_module/test_submodule.py,sha256=O07fFbzJEy5rf1vdlYzPvs8v0IxHFg-CaYMqaHkskbc,1294
|
|
25
|
+
datatailr-0.1.2.dist-info/METADATA,sha256=yfNet0SvA-XFRMQa8YYFJRduScQFdB3j4wVEz7XU3oQ,774
|
|
26
|
+
datatailr-0.1.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
27
|
+
datatailr-0.1.2.dist-info/entry_points.txt,sha256=4lNE9VXvztJdIQsODI308v9FRzkVgmMu-VKGEceNxJs,59
|
|
28
|
+
datatailr-0.1.2.dist-info/top_level.txt,sha256=UZOKaWS1kZGGwV7hP476-EcSUJBNspxVSSp9WqtORzk,22
|
|
29
|
+
datatailr-0.1.2.dist-info/RECORD,,
|
test_module/__init__.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# *************************************************************************
|
|
2
|
+
#
|
|
3
|
+
# Copyright (c) 2025 - Datatailr Inc.
|
|
4
|
+
# All Rights Reserved.
|
|
5
|
+
#
|
|
6
|
+
# This file is part of Datatailr and subject to the terms and conditions
|
|
7
|
+
# defined in 'LICENSE.txt'. Unauthorized copying and/or distribution
|
|
8
|
+
# of this file, in parts or full, via any medium is strictly prohibited.
|
|
9
|
+
# *************************************************************************
|
|
10
|
+
|
|
11
|
+
from .test_submodule import foo
|
|
12
|
+
from .test_submodule import test_function as test_function
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"test_function",
|
|
16
|
+
"foo",
|
|
17
|
+
]
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# *************************************************************************
|
|
2
|
+
#
|
|
3
|
+
# Copyright (c) 2025 - Datatailr Inc.
|
|
4
|
+
# All Rights Reserved.
|
|
5
|
+
#
|
|
6
|
+
# This file is part of Datatailr and subject to the terms and conditions
|
|
7
|
+
# defined in 'LICENSE.txt'. Unauthorized copying and/or distribution
|
|
8
|
+
# of this file, in parts or full, via any medium is strictly prohibited.
|
|
9
|
+
# *************************************************************************
|
|
10
|
+
|
|
11
|
+
from datatailr.logging import DatatailrLogger
|
|
12
|
+
from datatailr.scheduler import batch
|
|
13
|
+
|
|
14
|
+
logger = DatatailrLogger(__name__).get_logger()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@batch()
|
|
18
|
+
def foo():
|
|
19
|
+
logger.info(f"Running foo from {__name__}")
|
|
20
|
+
return "Hello from foo in test_submodule"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@batch()
|
|
24
|
+
def test_function(a, b, rundate=None):
|
|
25
|
+
"""Test function for the submodule."""
|
|
26
|
+
logger.info(f"Running test_function from test_submodule, {__name__}")
|
|
27
|
+
logger.info(f"Arguments: a={a}, b={b}, rundate={rundate}")
|
|
28
|
+
|
|
29
|
+
return f"args: ({a}, {b}, {rundate}), kwargs: {{}}"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@batch()
|
|
33
|
+
def another_test_function(x, y, z=None, rundate=None):
|
|
34
|
+
"""Another test function for the submodule."""
|
|
35
|
+
logger.info(f"Running another_test_function from test_submodule, {__name__}")
|
|
36
|
+
logger.info(f"Arguments: x={x}, y={y}, z={z}, rundate={rundate}")
|
|
37
|
+
|
|
38
|
+
return f"args: ({x}, {y}, {z}), kwargs: {{}}"
|
datatailr-0.1.1.dist-info/RECORD
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
datatailr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
datatailr-0.1.1.dist-info/licenses/LICENSE,sha256=ikKP4_O-UD_b8FuNdKmbzTb6odd0JX085ZW_FAPN3VI,1066
|
|
3
|
-
datatailr-0.1.1.dist-info/METADATA,sha256=qZ6zyDolR8qBoeVZ0_f_odTCbeGolxG6WX2OY29QPJA,596
|
|
4
|
-
datatailr-0.1.1.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
|
|
5
|
-
datatailr-0.1.1.dist-info/top_level.txt,sha256=75gntW0X_SKpqxLL6hAPipvpk28GAhJBvoyqN_HohWU,10
|
|
6
|
-
datatailr-0.1.1.dist-info/RECORD,,
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
datatailr
|
|
File without changes
|