schd 0.0.16__py3-none-any.whl → 0.1.0__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.
- schd/__init__.py +1 -1
- schd/cmds/base.py +1 -1
- schd/cmds/jobs.py +8 -5
- schd/cmds/run.py +14 -13
- schd/cmds/schd.py +9 -3
- schd/cmds/scsendmail.py +87 -0
- schd/config.py +57 -7
- schd/email.py +71 -0
- schd/scheduler.py +13 -3
- {schd-0.0.16.dist-info → schd-0.1.0.dist-info}/METADATA +11 -9
- schd-0.1.0.dist-info/RECORD +20 -0
- {schd-0.0.16.dist-info → schd-0.1.0.dist-info}/WHEEL +1 -1
- {schd-0.0.16.dist-info → schd-0.1.0.dist-info}/entry_points.txt +1 -0
- schd-0.0.16.dist-info/RECORD +0 -18
- {schd-0.0.16.dist-info → schd-0.1.0.dist-info}/top_level.txt +0 -0
schd/__init__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = '0.0
|
1
|
+
__version__ = '0.1.0'
|
schd/cmds/base.py
CHANGED
schd/cmds/jobs.py
CHANGED
@@ -1,16 +1,19 @@
|
|
1
1
|
"""
|
2
2
|
list jobs
|
3
3
|
"""
|
4
|
-
|
5
|
-
from schd.scheduler import read_config
|
4
|
+
import sys
|
6
5
|
from .base import CommandBase
|
7
6
|
|
8
7
|
|
9
8
|
class JobsCommand(CommandBase):
|
10
9
|
def add_arguments(self, parser):
|
11
|
-
parser.add_argument('--config', '-c', default=None, help='config file')
|
10
|
+
# parser.add_argument('--config', '-c', default=None, help='config file')
|
11
|
+
pass
|
12
|
+
|
13
|
+
def run(self, args, config=None):
|
14
|
+
if config is None:
|
15
|
+
print("No configuration provided.")
|
16
|
+
sys.exit(1)
|
12
17
|
|
13
|
-
def run(self, args):
|
14
|
-
config = read_config(config_file=args.config)
|
15
18
|
for job_name, _ in config.jobs.items():
|
16
19
|
print(job_name)
|
schd/cmds/run.py
CHANGED
@@ -1,26 +1,27 @@
|
|
1
|
+
import asyncio
|
1
2
|
import logging
|
3
|
+
import sys
|
2
4
|
from schd.cmds.base import CommandBase
|
3
|
-
from schd.scheduler import
|
5
|
+
from schd.scheduler import LocalScheduler, build_job
|
4
6
|
|
5
7
|
|
6
|
-
def run_job(
|
7
|
-
|
8
|
-
|
8
|
+
async def run_job(config, job_name):
|
9
|
+
scheduler = LocalScheduler(config)
|
9
10
|
job_config = config.jobs[job_name]
|
10
|
-
|
11
11
|
job = build_job(job_name, job_config.cls, job_config)
|
12
|
-
|
13
|
-
|
14
|
-
job(context=job_context)
|
12
|
+
await scheduler.add_job(job, job_name, job_config)
|
13
|
+
scheduler.execute_job(job_name)
|
15
14
|
|
16
15
|
|
17
16
|
class RunCommand(CommandBase):
|
18
17
|
def add_arguments(self, parser):
|
19
18
|
parser.add_argument('job')
|
20
|
-
parser.add_argument('--config', '-c')
|
21
19
|
|
22
|
-
def run(self, args):
|
23
|
-
|
20
|
+
def run(self, args, config):
|
21
|
+
if config is None:
|
22
|
+
print("No configuration provided.")
|
23
|
+
sys.exit(1)
|
24
|
+
|
25
|
+
logging.basicConfig(format='%(asctime)s %(name)s - %(levelname)s %(message)s', datefmt='%Y-%m-%d %H:%M:%S', level=logging.INFO)
|
24
26
|
job_name = args.job
|
25
|
-
|
26
|
-
run_job(config_file, job_name)
|
27
|
+
asyncio.run(run_job(config, job_name))
|
schd/cmds/schd.py
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
import argparse
|
2
2
|
import sys
|
3
3
|
from schd.cmds.jobs import JobsCommand
|
4
|
-
from schd.
|
4
|
+
from schd.config import ConfigFileNotFound, read_config
|
5
|
+
from schd import __version__ as schd_version
|
5
6
|
from .daemon import DaemonCommand
|
6
7
|
from .run import RunCommand
|
7
|
-
|
8
|
+
|
8
9
|
|
9
10
|
commands = {
|
10
11
|
'daemon': DaemonCommand(),
|
@@ -16,6 +17,7 @@ def main():
|
|
16
17
|
sys.path.append('.')
|
17
18
|
parser = argparse.ArgumentParser('schd')
|
18
19
|
parser.add_argument('--version', action='store_true', default=False)
|
20
|
+
parser.add_argument('--config')
|
19
21
|
sub_command_parsers = parser.add_subparsers(dest='cmd')
|
20
22
|
|
21
23
|
for cmd, cmd_obj in commands.items():
|
@@ -23,6 +25,10 @@ def main():
|
|
23
25
|
cmd_obj.add_arguments(sub_command_parser)
|
24
26
|
|
25
27
|
args = parser.parse_args()
|
28
|
+
try:
|
29
|
+
config = read_config(args.config)
|
30
|
+
except ConfigFileNotFound:
|
31
|
+
config = None
|
26
32
|
|
27
33
|
if args.version:
|
28
34
|
print('schd version ', schd_version)
|
@@ -32,7 +38,7 @@ def main():
|
|
32
38
|
parser.print_help()
|
33
39
|
return
|
34
40
|
|
35
|
-
commands[args.cmd].run(args)
|
41
|
+
commands[args.cmd].run(args, config=config)
|
36
42
|
|
37
43
|
|
38
44
|
if __name__ == '__main__':
|
schd/cmds/scsendmail.py
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
"""
|
2
|
+
scsendmail - Send email via command line using EmailService
|
3
|
+
|
4
|
+
Usage:
|
5
|
+
scsendmail --to someone@example.com --title "Report" --content "Text body"
|
6
|
+
scsendmail --to a@example.com --content-html-file ./body.html -a report.pdf
|
7
|
+
"""
|
8
|
+
|
9
|
+
import argparse
|
10
|
+
import logging
|
11
|
+
from pathlib import Path
|
12
|
+
from typing import List, Optional
|
13
|
+
import sys
|
14
|
+
|
15
|
+
from schd.config import read_config, ConfigFileNotFound, EmailConfig
|
16
|
+
from schd.email import EmailService
|
17
|
+
|
18
|
+
|
19
|
+
def parse_recipients(values: Optional[List[str]]) -> List[str]:
|
20
|
+
if not values:
|
21
|
+
return []
|
22
|
+
emails = []
|
23
|
+
for val in values:
|
24
|
+
emails.extend(email.strip() for email in val.split(',') if email.strip())
|
25
|
+
return emails
|
26
|
+
|
27
|
+
|
28
|
+
def main():
|
29
|
+
parser = argparse.ArgumentParser(description='scsendmail command')
|
30
|
+
parser.add_argument('--title', default='report', help='Email subject')
|
31
|
+
parser.add_argument('--content', default='no content', help='Plain text content')
|
32
|
+
parser.add_argument('--content-html-file', help='Path to HTML file for HTML content')
|
33
|
+
parser.add_argument('--to', dest='recipients', action='append', help='To recipients (comma-separated or multiple flags)')
|
34
|
+
parser.add_argument('--cc', action='append', help='CC recipients (comma-separated or multiple flags)')
|
35
|
+
parser.add_argument('--bcc', action='append', help='BCC recipients (comma-separated or multiple flags)')
|
36
|
+
parser.add_argument('--add-attach', '-a', action='append', dest='attachments', help='Attachment file paths')
|
37
|
+
parser.add_argument('--debug', action='store_true', default=False, help='Print instead of sending')
|
38
|
+
parser.add_argument('--config')
|
39
|
+
parser.add_argument('--loglevel', default='INFO', help='Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)')
|
40
|
+
|
41
|
+
args = parser.parse_args()
|
42
|
+
|
43
|
+
logging.basicConfig(level=args.loglevel.upper(),
|
44
|
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
45
|
+
|
46
|
+
# Load HTML content if provided
|
47
|
+
html_content = None
|
48
|
+
if args.content_html_file:
|
49
|
+
try:
|
50
|
+
html_content = Path(args.content_html_file).read_text(encoding='utf-8')
|
51
|
+
except Exception as e:
|
52
|
+
print(f"Failed to read HTML content file: {e}", file=sys.stderr)
|
53
|
+
sys.exit(1)
|
54
|
+
|
55
|
+
# Load config from environment or config file
|
56
|
+
schd_config = read_config(args.config)
|
57
|
+
logging.debug(schd_config.email)
|
58
|
+
service = EmailService.from_config(schd_config.email)
|
59
|
+
|
60
|
+
to_emails = parse_recipients(args.recipients)
|
61
|
+
cc_emails = parse_recipients(args.cc)
|
62
|
+
bcc_emails = parse_recipients(args.bcc)
|
63
|
+
attachments = args.attachments or []
|
64
|
+
|
65
|
+
if args.debug:
|
66
|
+
print("DEBUG MODE: Email will not be sent")
|
67
|
+
print(f"Subject: {args.title}")
|
68
|
+
print(f"To: {to_emails}")
|
69
|
+
print(f"CC: {cc_emails}")
|
70
|
+
print(f"BCC: {bcc_emails}")
|
71
|
+
print(f"Attachments: {attachments}")
|
72
|
+
print(f"Content: {args.content}")
|
73
|
+
print(f"HTML Content File: {args.content_html_file}")
|
74
|
+
else:
|
75
|
+
service.send_mail(
|
76
|
+
title=args.title,
|
77
|
+
content=args.content,
|
78
|
+
content_html=html_content,
|
79
|
+
to_emails=to_emails,
|
80
|
+
cc_emails=cc_emails,
|
81
|
+
bcc_emails=bcc_emails,
|
82
|
+
attachments=attachments
|
83
|
+
)
|
84
|
+
|
85
|
+
|
86
|
+
if __name__ == '__main__':
|
87
|
+
main()
|
schd/config.py
CHANGED
@@ -28,15 +28,20 @@ class ConfigValue:
|
|
28
28
|
type_hints = get_type_hints(cls)
|
29
29
|
init_data:Dict[str,Any] = {}
|
30
30
|
if not is_dataclass(cls):
|
31
|
-
raise TypeError('class
|
31
|
+
raise TypeError(f'class {cls} is not dataclass')
|
32
32
|
|
33
33
|
for f in fields(cls):
|
34
34
|
field_name = f.name
|
35
35
|
json_key = f.metadata.get("json", f.name)
|
36
|
+
envvar_key = f.metadata.get('env_var')
|
36
37
|
field_type = type_hints[field_name]
|
37
38
|
origin = get_origin(field_type)
|
38
39
|
args = get_args(field_type)
|
39
40
|
|
41
|
+
if envvar_key and envvar_key in os.environ:
|
42
|
+
init_data[field_name] = _cast_type(os.environ[envvar_key], field_type)
|
43
|
+
continue
|
44
|
+
|
40
45
|
if json_key in data:
|
41
46
|
value = data[json_key]
|
42
47
|
# Handle nested ConfigValue objects
|
@@ -63,6 +68,44 @@ class ConfigValue:
|
|
63
68
|
init_data[field_name] = value
|
64
69
|
return cls(**init_data)
|
65
70
|
|
71
|
+
def _cast_type(value, target_type):
|
72
|
+
origin = get_origin(target_type)
|
73
|
+
args = get_args(target_type)
|
74
|
+
|
75
|
+
# Handle Optional[T] or Union[T1, T2, ...]
|
76
|
+
if origin is Union:
|
77
|
+
# Optional[str] is Union[str, NoneType]
|
78
|
+
for typ in args:
|
79
|
+
if typ is type(None):
|
80
|
+
continue # skip NoneType
|
81
|
+
try:
|
82
|
+
return _cast_type(value, typ)
|
83
|
+
except (ValueError, TypeError):
|
84
|
+
continue
|
85
|
+
raise ValueError(f"Cannot cast {value!r} to any of {args}")
|
86
|
+
|
87
|
+
# Handle base types
|
88
|
+
if target_type == bool:
|
89
|
+
return value.lower() in ('true', '1', 'yes', 'on', True)
|
90
|
+
elif target_type == int:
|
91
|
+
return int(value)
|
92
|
+
elif target_type == float:
|
93
|
+
return float(value)
|
94
|
+
elif target_type == str:
|
95
|
+
return value
|
96
|
+
else:
|
97
|
+
raise TypeError(f"Unsupported type: {target_type}")
|
98
|
+
|
99
|
+
@dataclass
|
100
|
+
class EmailConfig(ConfigValue):
|
101
|
+
smtp_server: Optional[str] = field(metadata={'env_var': 'SCHD_SMTP_SERVER'}, default=None)
|
102
|
+
smtp_user: Optional[str] = field(metadata={'env_var': 'SCHD_SMTP_USER'}, default=None)
|
103
|
+
smtp_password: Optional[str] = field(metadata={'env_var': 'SCHD_SMTP_PASS'}, default=None)
|
104
|
+
from_addr: Optional[str] = field(metadata={'env_var': 'SCHD_SMTP_FROM'}, default=None)
|
105
|
+
to_addr: Optional[str] = field(metadata={'env_var': 'SCHD_SMTP_TO'}, default=None)
|
106
|
+
smtp_port: int = field(metadata={'env_var': 'SCHD_SMTP_PORT'}, default=25)
|
107
|
+
smtp_starttls: bool = field(metadata={'env_var': 'SCHD_SMTP_TLS'}, default=False)
|
108
|
+
|
66
109
|
|
67
110
|
@dataclass
|
68
111
|
class JobConfig(ConfigValue):
|
@@ -80,6 +123,7 @@ class SchdConfig(ConfigValue):
|
|
80
123
|
scheduler_cls: str = 'LocalScheduler'
|
81
124
|
scheduler_remote_host: Optional[str] = None
|
82
125
|
worker_name: str = 'local'
|
126
|
+
email: EmailConfig = field(default_factory=lambda: EmailConfig.from_dict({}))
|
83
127
|
|
84
128
|
def __getitem__(self,key):
|
85
129
|
# compatible to old fashion config['key']
|
@@ -89,13 +133,19 @@ class SchdConfig(ConfigValue):
|
|
89
133
|
raise KeyError(key)
|
90
134
|
|
91
135
|
|
92
|
-
|
93
|
-
if config_file is None and 'SCHD_CONFIG' in os.environ:
|
94
|
-
config_file = os.environ['SCHD_CONFIG']
|
136
|
+
class ConfigFileNotFound(Exception):...
|
95
137
|
|
96
|
-
if config_file is None:
|
97
|
-
config_file = 'conf/schd.yaml'
|
98
138
|
|
99
|
-
|
139
|
+
def read_config(config_file=None) -> SchdConfig:
|
140
|
+
if config_file:
|
141
|
+
config_filepath = config_file
|
142
|
+
elif 'SCHD_CONFIG' in os.environ:
|
143
|
+
config_filepath = os.environ['SCHD_CONFIG']
|
144
|
+
elif os.path.exists('conf/schd.yaml'):
|
145
|
+
config_filepath = 'conf/schd.yaml'
|
146
|
+
else:
|
147
|
+
raise ConfigFileNotFound()
|
148
|
+
|
149
|
+
with open(config_filepath, 'r', encoding='utf8') as f:
|
100
150
|
config = SchdConfig.from_dict(yaml.load(f, Loader=yaml.FullLoader))
|
101
151
|
return config
|
schd/email.py
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
import smtplib
|
2
|
+
from email.message import EmailMessage
|
3
|
+
import logging
|
4
|
+
from typing import List, Optional, Union
|
5
|
+
import os
|
6
|
+
from pathlib import Path
|
7
|
+
from schd.config import EmailConfig
|
8
|
+
|
9
|
+
logger = logging.getLogger(__name__)
|
10
|
+
|
11
|
+
|
12
|
+
class EmailService:
|
13
|
+
def __init__(self, smtp_server: str, smtp_user: str, smtp_password: str,
|
14
|
+
from_addr: str, smtp_port: int = 25, smtp_starttls: bool = False):
|
15
|
+
self.smtp_server = smtp_server
|
16
|
+
self.smtp_user = smtp_user
|
17
|
+
self.smtp_password = smtp_password
|
18
|
+
self.from_addr = from_addr
|
19
|
+
self.smtp_port = smtp_port
|
20
|
+
self.smtp_starttls = smtp_starttls
|
21
|
+
|
22
|
+
def send_mail(self, title: str, content: str, to_emails: Union[str, List[str]],
|
23
|
+
attachments: Optional[List[str]] = None,
|
24
|
+
content_html: Optional[str] = None,
|
25
|
+
cc_emails: Optional[List[str]] = None,
|
26
|
+
bcc_emails: Optional[List[str]] = None):
|
27
|
+
msg = EmailMessage()
|
28
|
+
msg['Subject'] = title
|
29
|
+
msg['From'] = self.from_addr
|
30
|
+
if isinstance(to_emails, str):
|
31
|
+
to_emails = [to_emails]
|
32
|
+
msg['To'] = ', '.join(to_emails)
|
33
|
+
if cc_emails:
|
34
|
+
msg['Cc'] = ', '.join(cc_emails)
|
35
|
+
|
36
|
+
recipients = to_emails + (cc_emails or []) + (bcc_emails or [])
|
37
|
+
|
38
|
+
# Add text and HTML
|
39
|
+
if content_html:
|
40
|
+
msg.set_content(content)
|
41
|
+
msg.add_alternative(content_html, subtype='html')
|
42
|
+
else:
|
43
|
+
msg.set_content(content)
|
44
|
+
|
45
|
+
# Attach files
|
46
|
+
for filepath in attachments or []:
|
47
|
+
file_path = Path(filepath)
|
48
|
+
with open(file_path, 'rb') as f:
|
49
|
+
file_data = f.read()
|
50
|
+
msg.add_attachment(file_data, maintype='application', subtype='octet-stream', filename=file_path.name)
|
51
|
+
|
52
|
+
# Send email
|
53
|
+
with smtplib.SMTP(self.smtp_server, self.smtp_port) as server:
|
54
|
+
if self.smtp_starttls:
|
55
|
+
server.starttls()
|
56
|
+
if self.smtp_user and self.smtp_password:
|
57
|
+
server.login(self.smtp_user, self.smtp_password)
|
58
|
+
else:
|
59
|
+
logger.info('no username/pass, skip logging in.')
|
60
|
+
server.send_message(msg, from_addr=self.from_addr, to_addrs=recipients)
|
61
|
+
|
62
|
+
@classmethod
|
63
|
+
def from_config(cls, config: 'EmailConfig') -> 'EmailService':
|
64
|
+
return cls(
|
65
|
+
smtp_server=config.smtp_server,
|
66
|
+
smtp_user=config.smtp_user,
|
67
|
+
smtp_password=config.smtp_password,
|
68
|
+
from_addr=config.from_addr,
|
69
|
+
smtp_port=config.smtp_port,
|
70
|
+
smtp_starttls=config.smtp_starttls
|
71
|
+
)
|
schd/scheduler.py
CHANGED
@@ -5,6 +5,7 @@ import logging
|
|
5
5
|
import importlib
|
6
6
|
import io
|
7
7
|
import os
|
8
|
+
import socket
|
8
9
|
import sys
|
9
10
|
from typing import Any, Optional, Dict
|
10
11
|
import smtplib
|
@@ -16,6 +17,7 @@ from apscheduler.schedulers.blocking import BlockingScheduler
|
|
16
17
|
from apscheduler.triggers.cron import CronTrigger
|
17
18
|
from apscheduler.executors.pool import ThreadPoolExecutor
|
18
19
|
from schd import __version__ as schd_version
|
20
|
+
from schd.email import EmailService
|
19
21
|
from schd.schedulers.remote import RemoteScheduler
|
20
22
|
from schd.util import ensure_bool
|
21
23
|
from schd.job import Job, JobContext, JobExecutionResult
|
@@ -179,7 +181,7 @@ class ConsoleErrorNotifier:
|
|
179
181
|
|
180
182
|
|
181
183
|
class LocalScheduler:
|
182
|
-
def __init__(self, max_concurrent_jobs: int = 10):
|
184
|
+
def __init__(self, config:SchdConfig, max_concurrent_jobs: int = 10):
|
183
185
|
"""
|
184
186
|
Initialize the LocalScheduler with support for concurrent job execution.
|
185
187
|
|
@@ -190,6 +192,9 @@ class LocalScheduler:
|
|
190
192
|
}
|
191
193
|
self.scheduler = BlockingScheduler(executors=executors)
|
192
194
|
self._jobs:Dict[str, Job] = {}
|
195
|
+
self.email_service = EmailService.from_config(config.email)
|
196
|
+
self.to_mail = config.email.to_addr
|
197
|
+
self.worker_name = config.worker_name or socket.gethostname()
|
193
198
|
logger.info("LocalScheduler initialized in 'local' mode with concurrency support")
|
194
199
|
|
195
200
|
async def init(self):
|
@@ -234,8 +239,13 @@ class LocalScheduler:
|
|
234
239
|
logger.exception('error when executing job, %s', ex)
|
235
240
|
ret_code = -1
|
236
241
|
|
242
|
+
output = output_stream.getvalue()
|
237
243
|
logger.info('job %s execute complete: %d', job_name, ret_code)
|
238
|
-
logger.info('job %s process output: \n%s', job_name,
|
244
|
+
logger.info('job %s process output: \n%s', job_name, output)
|
245
|
+
if ret_code != 0 and self.to_mail:
|
246
|
+
self.email_service.send_mail('job failed %s %s' % (self.worker_name, job_name),
|
247
|
+
content=output,
|
248
|
+
to_emails=self.to_mail)
|
239
249
|
|
240
250
|
def run(self):
|
241
251
|
"""
|
@@ -255,7 +265,7 @@ def build_scheduler(config:SchdConfig):
|
|
255
265
|
scheduler_cls = os.environ.get('SCHD_SCHEDULER_CLS') or config.scheduler_cls
|
256
266
|
|
257
267
|
if scheduler_cls == 'LocalScheduler':
|
258
|
-
scheduler = LocalScheduler()
|
268
|
+
scheduler = LocalScheduler(config)
|
259
269
|
elif scheduler_cls == 'RemoteScheduler':
|
260
270
|
logger.info('scheduler_cls: %s', scheduler_cls)
|
261
271
|
scheduler_remote_host = os.environ.get('SCHD_SCHEDULER_REMOTE_HOST') or config.scheduler_remote_host
|
@@ -1,9 +1,11 @@
|
|
1
|
-
Metadata-Version: 2.
|
2
|
-
Name: schd
|
3
|
-
Version: 0.0
|
4
|
-
Home-page: https://github.com/kevenli/schd
|
5
|
-
License: ApacheV2
|
6
|
-
Requires-Dist: apscheduler<4.0
|
7
|
-
Requires-Dist: pyaml
|
8
|
-
Requires-Dist: aiohttp
|
9
|
-
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: schd
|
3
|
+
Version: 0.1.0
|
4
|
+
Home-page: https://github.com/kevenli/schd
|
5
|
+
License: ApacheV2
|
6
|
+
Requires-Dist: apscheduler<4.0
|
7
|
+
Requires-Dist: pyaml
|
8
|
+
Requires-Dist: aiohttp
|
9
|
+
Dynamic: home-page
|
10
|
+
Dynamic: license
|
11
|
+
Dynamic: requires-dist
|
@@ -0,0 +1,20 @@
|
|
1
|
+
schd/__init__.py,sha256=ryAfvAaW7VM8g1gnCrFCPrXmcbbm99Xw44aLkwGZzaI,23
|
2
|
+
schd/config.py,sha256=YjwqUFkULjNONDw-Q0QQeNnWQK3XMGHuGSjAHZMGwh8,5981
|
3
|
+
schd/email.py,sha256=eBKzNnuQxcJwlWOeXcAMNkGZlUa3O5zM7amfUW7h3gE,2633
|
4
|
+
schd/job.py,sha256=AoW-2W1hRY_O4nz_pKukgmEXYkjlP2XmYimyoGCs-Bw,614
|
5
|
+
schd/scheduler.py,sha256=9NH0p1HdSl69DUhDrWUYItpepKogpvDY3BAu37SxMpo,13479
|
6
|
+
schd/util.py,sha256=NH4EqIns1Y01yz1Rf3cw-tlKSLc-XNuS9hHbAdq5D_I,592
|
7
|
+
schd/cmds/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
|
+
schd/cmds/base.py,sha256=lsVIqE1m-uBRRNt7UDYJIIthYR7pyINccfS5hVK3BWA,130
|
9
|
+
schd/cmds/daemon.py,sha256=MReUf8TDyE-zEUjJ1T1v4RBD8f2edpE7TeGdooaHD6Y,889
|
10
|
+
schd/cmds/jobs.py,sha256=F3ArEeBTOiFpe3gmtkFg6kRtoYCjQGp4kAc3r6UJbtA,459
|
11
|
+
schd/cmds/run.py,sha256=woTK4C2h6D5JEYcwXOzS9IHuTVtaTKWMf5jfFhikW2k,888
|
12
|
+
schd/cmds/schd.py,sha256=anVfbXXElKRI5Zd8sJYYroBfyqS35RGAtXD5nTEv6ag,1164
|
13
|
+
schd/cmds/scsendmail.py,sha256=WIQzzXOFfh8Rwxtzdbn7HH14oG0jUrbxLyA3KL10yM4,3285
|
14
|
+
schd/schedulers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
15
|
+
schd/schedulers/remote.py,sha256=iaQmddf-g095jAIJAGAn0mQKrLRfu9gNZFvE0NkJVy0,8087
|
16
|
+
schd-0.1.0.dist-info/METADATA,sha256=-P3j6UG5Ku1e_x-W9hf7NaW52K4eRlMKtA9GxFMLx3Q,243
|
17
|
+
schd-0.1.0.dist-info/WHEEL,sha256=DnLRTWE75wApRYVsjgc6wsVswC54sMSJhAEd4xhDpBk,91
|
18
|
+
schd-0.1.0.dist-info/entry_points.txt,sha256=lY18p1hQAKgv47FBqPj37z8o4Kr1MHnbGlsrNLS4ZKs,84
|
19
|
+
schd-0.1.0.dist-info/top_level.txt,sha256=Vojim8xSOsYyQHrZBNhx7n0F7WqbEJ2SeBFAvEnrJ_U,5
|
20
|
+
schd-0.1.0.dist-info/RECORD,,
|
schd-0.0.16.dist-info/RECORD
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
schd/__init__.py,sha256=3kGSHQKV1gPB0rPzBMZNIYjHiZim2MThuzEWpP2YZcg,24
|
2
|
-
schd/config.py,sha256=YBSRiXZApC_ylPYUM4cwTU7FjVqMHwG3vPy-F6SA95k,3930
|
3
|
-
schd/job.py,sha256=AoW-2W1hRY_O4nz_pKukgmEXYkjlP2XmYimyoGCs-Bw,614
|
4
|
-
schd/scheduler.py,sha256=EYIU7aX8qs-RYS_eXJc-JZU3e1T-mTMzkEv51b-AXgM,12931
|
5
|
-
schd/util.py,sha256=NH4EqIns1Y01yz1Rf3cw-tlKSLc-XNuS9hHbAdq5D_I,592
|
6
|
-
schd/cmds/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
|
-
schd/cmds/base.py,sha256=ZnNcJozQFLbpYyNp8dhHzm3BzFQa0hm6KyCC6URfueY,122
|
8
|
-
schd/cmds/daemon.py,sha256=MReUf8TDyE-zEUjJ1T1v4RBD8f2edpE7TeGdooaHD6Y,889
|
9
|
-
schd/cmds/jobs.py,sha256=DwAuTxZHjqMsG952IpaReTzCjvBII44bZquriFncl1Q,411
|
10
|
-
schd/cmds/run.py,sha256=Z-fO-Qk9RS9lBAszmPuuJbVPczIU7AA40Qnd0ngmgsI,752
|
11
|
-
schd/cmds/schd.py,sha256=vOlfQCQT81KhLMvlP3tlymEWpxmqlBOa5vnJoeFXVlw,996
|
12
|
-
schd/schedulers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
13
|
-
schd/schedulers/remote.py,sha256=iaQmddf-g095jAIJAGAn0mQKrLRfu9gNZFvE0NkJVy0,8087
|
14
|
-
schd-0.0.16.dist-info/METADATA,sha256=OUyzeY7EMMeUkGCW74DmnhYwJjrl_bBJbJCM6xOa6GI,195
|
15
|
-
schd-0.0.16.dist-info/WHEEL,sha256=iAkIy5fosb7FzIOwONchHf19Qu7_1wCWyFNR5gu9nU0,91
|
16
|
-
schd-0.0.16.dist-info/entry_points.txt,sha256=VvUhIaucvHlggoz-4lWtQgXbDwoWH9x-iT0QOUHEMyI,45
|
17
|
-
schd-0.0.16.dist-info/top_level.txt,sha256=Vojim8xSOsYyQHrZBNhx7n0F7WqbEJ2SeBFAvEnrJ_U,5
|
18
|
-
schd-0.0.16.dist-info/RECORD,,
|
File without changes
|