qdir 1.0.0rc1__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.
qdir-1.0.0rc1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Carl Osterwisch
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
qdir-1.0.0rc1/PKG-INFO ADDED
@@ -0,0 +1,112 @@
1
+ Metadata-Version: 2.4
2
+ Name: qdir
3
+ Version: 1.0.0rc1
4
+ Summary: A very low overhead first-in, first-out job queue for the local machine
5
+ Author-email: Carl Osterwisch <costerwi@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/costerwi/qdir
8
+ Project-URL: Issues, https://github.com/costerwi/qdir/issues
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.6
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: filelock
15
+ Requires-Dist: pywin32; sys_platform == "win32"
16
+ Dynamic: license-file
17
+
18
+ # qdir
19
+ A very low overhead first-in, first-out job queue for the local machine
20
+
21
+ The queue is maintained as a directory of tiny job queue files.
22
+ When a new job is submitted, this script will identify any existing job queue files and then add its own queue file to the directory.
23
+ No job will begin until all the preceeding queue files are gone.
24
+ Each job will delete its own queue file upon completion so the next job can begin.
25
+ A shared queue directory can be specified on a multi-user system.
26
+ On Linux this is `/tmp` by default.
27
+
28
+ Any command may be submitted to the job queue using qsub (see usage below).
29
+ You may check the status of the queue (qstat) and remove jobs (qdel).
30
+
31
+ # Installation
32
+ Requires python3 running on Windows or Linux.
33
+ One way to get python3 on Windows is through the [Microsoft Store](https://apps.microsoft.com/detail/9PNRBTZXMB4Z).
34
+ Here is the command to install qdir once you have python3 available:
35
+ ```bash
36
+ python3 -m pip install qdir
37
+ ```
38
+
39
+ See [here](https://pip.pypa.io/en/stable/installation/) if there is "no module named pip."
40
+
41
+ ## Abaqus Finite Element Analysis (FEA) extras
42
+ The package includes additional files to help Abaqus users submit and monitor the qdir job queue.
43
+ Use the following command to configure Abaqus after qdir itself has been installed:
44
+ ```bash
45
+ python3 -m qdir.abaqus install
46
+ ```
47
+
48
+ By default, the necessary settings are appended to the end of your user `abaqus_v6.env` file.
49
+ To uninstall you may removed the settings manually or with this command:
50
+ ```bash
51
+ python3 -m qdir.abaqus uninstall
52
+ ```
53
+
54
+ # Usage
55
+
56
+ ## Command line job submission
57
+ Submit commands or scripts to the queue using the `qsub` command.
58
+ For example, on Linux:
59
+ ```bash
60
+ qsub ./myJobFile.sh
61
+ ```
62
+ or on Windows:
63
+ ```bash
64
+ qsub myJobFile.cmd
65
+ ```
66
+
67
+ Any printed output from the command will be saved to a file based on the job name with the job Id appended to the end.
68
+
69
+ Display command options with `qsub -h`
70
+
71
+ ## Command line queue status
72
+ Check status with the `qstat` command, for example:
73
+ ```bash
74
+ qstat
75
+ ```
76
+ or, for full details:
77
+ ```bash
78
+ qstat -f
79
+ ```
80
+
81
+ ## Command line job removal
82
+ First use the `qstat` command above to find the jobIds and job names in the queue.
83
+ Delete or kill jobs with the `qdel` command followed by a jobId or job name.
84
+ For example, to delete job 12343:
85
+ ```bash
86
+ qdel 12343
87
+ ```
88
+
89
+ ## Abaqus extras
90
+ Submit Abaqus jobs as usual from the command line:
91
+ ```bash
92
+ abaqus job=whatever
93
+ ```
94
+ This will invoke the `qdir_submit.py` module to create a job script and submit it to qdir.
95
+
96
+ You may submit jobs within Abaqus CAE by selecting `qdir` as the Job's queue before submitting.
97
+ You may check the queue status within CAE in the menu `Plug-ins > QDir Job Queue Monitor...`
98
+ Click on a Job to see more detailed information.
99
+ Right-click on a Job to expose the menu allowing you to delete the Job.
100
+
101
+ ## Customization
102
+ Most parameters used by the script may be customized with command line options, system environment variables, or directives within the job file itself.
103
+
104
+ | Option | Description | Default |
105
+ |--------|-------------|---------|
106
+ | -N *NAME*, --name *NAME* | Name for the job. | The last argument of the command |
107
+ | -q *QUEUE*, --queue *QUEUE* | Directory to store the queue files. | environment variable `QDIR_QUEUE` or `/tmp` on Linux |
108
+ | -c *INTERVAL*, --interval *INTERVAL* | Polling interval in seconds. | environment variable `QDIR_POLL_INTERVAL` or 5 |
109
+ | -i *INFO*, --info *INFO* | Specify the format of information about jobs in queue | environment variable `QDIR_INFO` or `{jobId:8} {owner:12} {status:35} {name}` |
110
+ | -o *OUTPUT*, --output *OUTPUT* | File name for combined stdout and stderr. | environment variable `QDIR_LOGNAME` or `{name}._{jobId}`
111
+ | -s *STATUS*, --status *STATUS* | Specify the format of job status messages output to terminal. | environment variable `QDIR_STATUS` or `{jobId} {name} {status} after {duration}`
112
+ | -v *VERBOSE*, --verbose *VERBOSE* | Set the verbosity level. | environment variable `QDIR_VERBOSE` or 2 |
@@ -0,0 +1,95 @@
1
+ # qdir
2
+ A very low overhead first-in, first-out job queue for the local machine
3
+
4
+ The queue is maintained as a directory of tiny job queue files.
5
+ When a new job is submitted, this script will identify any existing job queue files and then add its own queue file to the directory.
6
+ No job will begin until all the preceeding queue files are gone.
7
+ Each job will delete its own queue file upon completion so the next job can begin.
8
+ A shared queue directory can be specified on a multi-user system.
9
+ On Linux this is `/tmp` by default.
10
+
11
+ Any command may be submitted to the job queue using qsub (see usage below).
12
+ You may check the status of the queue (qstat) and remove jobs (qdel).
13
+
14
+ # Installation
15
+ Requires python3 running on Windows or Linux.
16
+ One way to get python3 on Windows is through the [Microsoft Store](https://apps.microsoft.com/detail/9PNRBTZXMB4Z).
17
+ Here is the command to install qdir once you have python3 available:
18
+ ```bash
19
+ python3 -m pip install qdir
20
+ ```
21
+
22
+ See [here](https://pip.pypa.io/en/stable/installation/) if there is "no module named pip."
23
+
24
+ ## Abaqus Finite Element Analysis (FEA) extras
25
+ The package includes additional files to help Abaqus users submit and monitor the qdir job queue.
26
+ Use the following command to configure Abaqus after qdir itself has been installed:
27
+ ```bash
28
+ python3 -m qdir.abaqus install
29
+ ```
30
+
31
+ By default, the necessary settings are appended to the end of your user `abaqus_v6.env` file.
32
+ To uninstall you may removed the settings manually or with this command:
33
+ ```bash
34
+ python3 -m qdir.abaqus uninstall
35
+ ```
36
+
37
+ # Usage
38
+
39
+ ## Command line job submission
40
+ Submit commands or scripts to the queue using the `qsub` command.
41
+ For example, on Linux:
42
+ ```bash
43
+ qsub ./myJobFile.sh
44
+ ```
45
+ or on Windows:
46
+ ```bash
47
+ qsub myJobFile.cmd
48
+ ```
49
+
50
+ Any printed output from the command will be saved to a file based on the job name with the job Id appended to the end.
51
+
52
+ Display command options with `qsub -h`
53
+
54
+ ## Command line queue status
55
+ Check status with the `qstat` command, for example:
56
+ ```bash
57
+ qstat
58
+ ```
59
+ or, for full details:
60
+ ```bash
61
+ qstat -f
62
+ ```
63
+
64
+ ## Command line job removal
65
+ First use the `qstat` command above to find the jobIds and job names in the queue.
66
+ Delete or kill jobs with the `qdel` command followed by a jobId or job name.
67
+ For example, to delete job 12343:
68
+ ```bash
69
+ qdel 12343
70
+ ```
71
+
72
+ ## Abaqus extras
73
+ Submit Abaqus jobs as usual from the command line:
74
+ ```bash
75
+ abaqus job=whatever
76
+ ```
77
+ This will invoke the `qdir_submit.py` module to create a job script and submit it to qdir.
78
+
79
+ You may submit jobs within Abaqus CAE by selecting `qdir` as the Job's queue before submitting.
80
+ You may check the queue status within CAE in the menu `Plug-ins > QDir Job Queue Monitor...`
81
+ Click on a Job to see more detailed information.
82
+ Right-click on a Job to expose the menu allowing you to delete the Job.
83
+
84
+ ## Customization
85
+ Most parameters used by the script may be customized with command line options, system environment variables, or directives within the job file itself.
86
+
87
+ | Option | Description | Default |
88
+ |--------|-------------|---------|
89
+ | -N *NAME*, --name *NAME* | Name for the job. | The last argument of the command |
90
+ | -q *QUEUE*, --queue *QUEUE* | Directory to store the queue files. | environment variable `QDIR_QUEUE` or `/tmp` on Linux |
91
+ | -c *INTERVAL*, --interval *INTERVAL* | Polling interval in seconds. | environment variable `QDIR_POLL_INTERVAL` or 5 |
92
+ | -i *INFO*, --info *INFO* | Specify the format of information about jobs in queue | environment variable `QDIR_INFO` or `{jobId:8} {owner:12} {status:35} {name}` |
93
+ | -o *OUTPUT*, --output *OUTPUT* | File name for combined stdout and stderr. | environment variable `QDIR_LOGNAME` or `{name}._{jobId}`
94
+ | -s *STATUS*, --status *STATUS* | Specify the format of job status messages output to terminal. | environment variable `QDIR_STATUS` or `{jobId} {name} {status} after {duration}`
95
+ | -v *VERBOSE*, --verbose *VERBOSE* | Set the verbosity level. | environment variable `QDIR_VERBOSE` or 2 |
@@ -0,0 +1,35 @@
1
+ [project]
2
+ name = "qdir"
3
+ authors = [
4
+ { name="Carl Osterwisch", email="costerwi@gmail.com" },
5
+ ]
6
+ description = "A very low overhead first-in, first-out job queue for the local machine"
7
+ readme = "README.md"
8
+ requires-python = ">=3.6"
9
+ classifiers = [
10
+ "Programming Language :: Python :: 3",
11
+ "Operating System :: OS Independent",
12
+ ]
13
+ license = "MIT"
14
+ license-files = ["LICENSE"]
15
+ dependencies = [
16
+ "filelock",
17
+ "pywin32; sys_platform == 'win32'",
18
+ ]
19
+ dynamic = ["version"]
20
+
21
+ [project.scripts]
22
+ qdel = "qdir.qdel:main"
23
+ qstat = "qdir.qstat:main"
24
+ qsub = "qdir.qsub:main"
25
+
26
+ [project.urls]
27
+ Homepage = "https://github.com/costerwi/qdir"
28
+ Issues = "https://github.com/costerwi/qdir/issues"
29
+
30
+ [build-system]
31
+ requires = ["setuptools"]
32
+ build-backend = "setuptools.build_meta"
33
+
34
+ [tool.setuptools.dynamic]
35
+ version = {attr = "qdir.__version__"}
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,275 @@
1
+ #!/bin/env python3
2
+ """Implementation of a first-in, first-out job queue for the local machine.
3
+
4
+ The queue is maintained as a directory of tiny job queue files.
5
+ When a new job is submitted, this script will identify any existing job queue files and then add its own queue file to the directory.
6
+ No job will begin until all the preceeding queue files are gone.
7
+ Each job will delete its own queue file upon completion so the next job can begin.
8
+ A shared queue directory should be specified on a multi-user system.
9
+ """
10
+
11
+ __author__ = "Carl Osterwisch"
12
+ __version__ = "1.0.0rc1"
13
+ __url__ = "https://github.com/costerwi/qdir/"
14
+
15
+ import os
16
+ import signal
17
+ import subprocess
18
+ import sys
19
+ from argparse import ArgumentParser, Namespace
20
+ from pathlib import Path
21
+ from tempfile import gettempdir
22
+ from time import asctime, perf_counter, sleep
23
+
24
+ from filelock import FileLock
25
+
26
+ default = Namespace
27
+
28
+ parser = ArgumentParser(epilog=f"qdir {__version__} {__url__}")
29
+ default.queue = Path(os.environ.get("QDIR_QUEUE", gettempdir()))
30
+ parser.add_argument(
31
+ "-q",
32
+ "--queue",
33
+ type=Path,
34
+ help="Directory to store the queue files. "
35
+ f"Default is environment variable QDIR_QUEUE or {default.queue}",
36
+ default=default.queue,
37
+ )
38
+ default.verbose = int(os.environ.get("QDIR_VERBOSE", "2"))
39
+ parser.add_argument(
40
+ "-v",
41
+ "--verbose",
42
+ type=int,
43
+ help="Verbosity level. "
44
+ f"Default is environment variable QDIR_VERBOSE or {default.verbose}",
45
+ default=default.verbose,
46
+ )
47
+
48
+ qFileSuffix = os.environ.get("QDIR_SUFFIX", ".qdir") # suffix for queue files
49
+
50
+
51
+ class Queue:
52
+ def __init__(self, **kwargs):
53
+ """Create the queue, allowing parameters to override defaults"""
54
+ for parameter, value in vars(default).items():
55
+ setattr(self, parameter, kwargs.pop(parameter, value))
56
+ if not self.queue.is_dir():
57
+ raise FileNotFoundError(f"The QDIR directory {self.queue} does not exist")
58
+ if not os.access(self.queue, os.W_OK):
59
+ raise PermissionError(f"The QDIR directory {self.queue} is not writable")
60
+
61
+ def getQueuedJobs(self) -> list[Path]:
62
+ """Return a list of qFilePath for currently queued jobs, sorted oldest to newest."""
63
+ # Collecct a set of active process ids
64
+ if os.name == "nt":
65
+ import win32process
66
+
67
+ processIds = {str(pid) for pid in win32process.EnumProcesses()}
68
+ else:
69
+ procPath = Path("/proc")
70
+ processIds = {p.stem for p in procPath.iterdir() if p.stem.isnumeric()}
71
+
72
+ def is_qFile(path):
73
+ """Verify the path is an active job file with a matching process id"""
74
+ if not path.stem.isnumeric():
75
+ return False # file name must be an integer
76
+ if not (path.is_file()):
77
+ return False # path must represent a file
78
+ if path.stem in processIds:
79
+ return True # file name must match a running process number
80
+
81
+ # Otherwise, the process may have died or the computer was rebooted with
82
+ # this job in the queue. This orphaned qFile may be removed.
83
+ print(
84
+ "Removing orphaned queue file",
85
+ self.info.format(**self.getJobInfo(path)),
86
+ file=sys.stderr,
87
+ )
88
+ try:
89
+ path.unlink(missing_ok=True)
90
+ except OSError as e:
91
+ print(e, file=sys.stderr)
92
+
93
+ queuedJobs = [
94
+ filePath
95
+ for filePath in self.queue.glob("*" + qFileSuffix)
96
+ if is_qFile(filePath)
97
+ ]
98
+ return sorted(queuedJobs, key=lambda qFilePath: qFilePath.stat().st_mtime)
99
+
100
+ def getJobInfo(self, qFilePath: Path) -> dict:
101
+ """Return information about the job specified in qFilePath"""
102
+ jobInfo = {
103
+ "jobId": qFilePath.stem,
104
+ "owner": qFilePath.owner(),
105
+ "name": "",
106
+ "command": "",
107
+ "directory": "",
108
+ "behind": "",
109
+ "status": "",
110
+ "history": [], # list[str]
111
+ }
112
+ try:
113
+ with qFilePath.open() as qFile:
114
+ for line in qFile:
115
+ keyValue = line.strip().split("\t", 2)
116
+ if len(keyValue) == 2:
117
+ jobInfo.update([keyValue])
118
+ else:
119
+ status = keyValue[0]
120
+ jobInfo["history"].append(status)
121
+ jobInfo["status"] = status # latest update
122
+ except OSError as e:
123
+ status = repr(e)
124
+ jobInfo["history"].append(status)
125
+ jobInfo["status"] = status
126
+ return jobInfo
127
+
128
+ def dequeue(self, qFilePath: Path):
129
+ """Remove a job from the queue"""
130
+ assert qFilePath.parent == self.queue
131
+ assert qFilePath.suffix == qFileSuffix
132
+ assert qFilePath.stem.isnumeric()
133
+ # Removing qFile will dequeue the job but the effect is delayed by polling interval
134
+ qFilePath.unlink(missing_ok=True)
135
+ if os.name == "posix":
136
+ # On Linux the SIGTERM signal has immediate effect
137
+ try:
138
+ os.kill(int(qFilePath.stem), signal.SIGTERM)
139
+ except OSError: # no such process
140
+ pass
141
+
142
+ def enqueue(self, cmd: list[str]):
143
+ """Wait in queue until prior jobs finish, then run cmd"""
144
+ import getpass
145
+ import shutil
146
+
147
+ if len(cmd) < 1:
148
+ raise SyntaxError("Missing cmd")
149
+ if self.verbose >= 1 and not shutil.which(cmd[0]):
150
+ print(f"WARNING: {cmd[0]} is not in PATH", file=sys.stderr)
151
+ if not self.interval > 0:
152
+ self.interval = 1
153
+
154
+ startTime = perf_counter() # time job was enqueued
155
+ job = None # subprocess running the cmd, if any
156
+
157
+ jobInfo = { # information about this job
158
+ "jobId": str(os.getpid()), # process id of this script
159
+ "owner": getpass.getuser(),
160
+ "name": self.name or cmd[-1],
161
+ "command": " ".join(cmd),
162
+ "directory": Path.cwd(),
163
+ }
164
+
165
+ def updateStatus(priority, status, file=None):
166
+ """Notify user of status change and optionally record in file"""
167
+ jobInfo["status"] = status
168
+ jobInfo["time"] = asctime()
169
+ jobInfo["duration"] = humanDuration(perf_counter() - startTime)
170
+ if self.verbose >= priority:
171
+ # Echo status to the terminal
172
+ print(self.status.format(**jobInfo))
173
+ if file is not None:
174
+ # Record status history in specified qFile
175
+ print("{time} {status}".format(**jobInfo), file=file)
176
+
177
+ qFilePath = (self.queue / jobInfo["jobId"]).with_suffix(qFileSuffix)
178
+ lock = FileLock(self.queue / "qdir.lock") # prevent previousJobs race
179
+ with lock.acquire(timeout=5):
180
+ previousJobs = self.getQueuedJobs()
181
+ jobInfo["behind"] = " ".join([job.stem for job in previousJobs])
182
+ with qFilePath.open("w") as qFile:
183
+ os.environ["QDIR_FILE"] = qFile.name # will be exported to cmd
184
+ # Initialize qFile contents
185
+ for key in "name", "command", "directory", "behind":
186
+ value = jobInfo.get(key)
187
+ if value:
188
+ print(key, value, sep="\t", file=qFile)
189
+ updateStatus(priority=2, status="Queued", file=qFile)
190
+
191
+ def terminateJob(sig=None, frame=None):
192
+ """Terminate this job if running or dequeue if not"""
193
+ nonlocal job
194
+ result = ""
195
+ qFilePath.unlink(missing_ok=True) # remove its qFile
196
+ if job is None:
197
+ # removed from the queue before starting
198
+ result = ".. Dequeued"
199
+ elif hasattr(job, "pid"): # job is or was running
200
+ if job.poll() is None:
201
+ # job is still running
202
+ if self.verbose >= 2:
203
+ result = ".. Killed"
204
+ if os.name == "nt":
205
+ job.send_signal(signal.CTRL_BREAK_EVENT)
206
+ else:
207
+ # pass SIGTERM signal to all child processes in this process group
208
+ os.killpg(os.getpgid(job.pid), signal.SIGTERM)
209
+ elif self.verbose >= 1:
210
+ # job is no longer running; check returncode
211
+ if job.returncode <= 0:
212
+ result = "** Done"
213
+ else:
214
+ result = "-- Error"
215
+ if result:
216
+ updateStatus(priority=2, status=result)
217
+ job = result # prevent this from duplicate reporting
218
+
219
+ signal.signal(
220
+ signal.SIGTERM, terminateJob
221
+ ) # catch termination signal (ignored on Windows)
222
+
223
+ # Wait for all previousJobs to complete
224
+ previousJobs.reverse() # check newest jobs first
225
+ while qFilePath.exists() and any(
226
+ otherFile.exists() for otherFile in previousJobs
227
+ ):
228
+ sleep(self.interval)
229
+
230
+ # Launch the cmd only if this job's qFile still exists
231
+ if qFilePath.exists():
232
+ try:
233
+ # Append the job starting status to qFile
234
+ stat = qFilePath.stat()
235
+ with qFilePath.open("a") as qFile:
236
+ print(
237
+ "Waited in queue",
238
+ humanDuration(perf_counter() - startTime),
239
+ file=qFile,
240
+ )
241
+ updateStatus(priority=3, status="Started", file=qFile)
242
+ # Keep mtime unchanged so the job files sort correctly
243
+ os.utime(qFilePath, (stat.st_atime, stat.st_mtime))
244
+ except OSError:
245
+ pass # ignore status update exceptions
246
+ logFilePath = Path(self.output.format(**jobInfo))
247
+ with logFilePath.open("w") as logFile:
248
+ opts = {}
249
+ if os.name == "nt":
250
+ opts["creationflags"] = subprocess.CREATE_NEW_PROCESS_GROUP
251
+ try:
252
+ job = subprocess.Popen(
253
+ cmd,
254
+ shell=False,
255
+ start_new_session=True,
256
+ stdin=subprocess.DEVNULL,
257
+ stdout=logFile,
258
+ stderr=subprocess.STDOUT,
259
+ **opts,
260
+ )
261
+ startTime = perf_counter() # reset to time job started running
262
+ while qFilePath.exists() and job.poll() is None:
263
+ # job is still running
264
+ sleep(self.interval)
265
+ except OSError as e:
266
+ print(e, file=logFile)
267
+
268
+ terminateJob() # Clean up at the end
269
+
270
+
271
+ def humanDuration(seconds: float) -> str:
272
+ for unit, per in ("days", 86400), ("hours", 3600), ("minutes", 60):
273
+ if seconds >= 1.1 * per:
274
+ return f"{seconds / per:.2g} {unit}"
275
+ return f"{seconds:.2g} seconds"
@@ -0,0 +1,5 @@
1
+ """Modules to help Abaqus interact with the qdir job queue
2
+
3
+ Install with:
4
+ python3 -m qdir.abaqus install
5
+ """
@@ -0,0 +1,66 @@
1
+ """Install or uninstall qdir for Abaqus.
2
+
3
+ This script will modify the user's abaqus_v6.env file with
4
+ settings for the qdir job queue.
5
+ """
6
+
7
+ from __future__ import print_function, with_statement # use Python3 syntax
8
+
9
+ import os
10
+ import sys
11
+ from argparse import ArgumentParser
12
+
13
+ settings = """# Settings for qdir job queue
14
+ sys.path.append(r"{}") # directory of qdir_submit.py
15
+ import qdir_submit
16
+ qdir_submit.init(queues)
17
+ plugin_central_dir += os.pathsep + qdir_submit.dirname
18
+ run_mode = BATCH # command line will submit to queue by default"""
19
+
20
+ parser = ArgumentParser(description=__doc__)
21
+ parser.add_argument(
22
+ "action",
23
+ choices=["install", "uninstall"],
24
+ help="Specify what to do.",
25
+ )
26
+ default = os.path.expanduser("~/abaqus_v6.env")
27
+ parser.add_argument(
28
+ "env",
29
+ nargs="?",
30
+ default=default,
31
+ help="Optionally specify the .env file to modify. Default is {}".format(default),
32
+ )
33
+
34
+
35
+ def uninstall(user_env):
36
+ """Remove qdir settings from user's abaqus_v6.env"""
37
+ new_env = user_env + ".new"
38
+ with open(new_env, "w") as new_env_file:
39
+ try:
40
+ # copy current settings
41
+ with open(user_env) as env_file:
42
+ for line in env_file:
43
+ if not "qdir" in line: # skip any qdir settings
44
+ print(line.rstrip(), file=new_env_file)
45
+ os.remove(user_env)
46
+ except FileNotFoundError:
47
+ pass
48
+ os.rename(new_env, user_env)
49
+
50
+
51
+ def install(user_env):
52
+ """Append qdir settings to user's abaqus_v6.env"""
53
+ uninstall(user_env) # remove any previous settings
54
+ dirname = os.path.dirname(os.path.normpath(__file__))
55
+ with open(user_env, "a") as user_env_file:
56
+ print(settings.format(dirname), file=user_env_file)
57
+
58
+
59
+ if __name__ == "__main__":
60
+ options = parser.parse_args(sys.argv[1:])
61
+ if "uninstall" == options.action:
62
+ print("Removing qdir settings from", options.env)
63
+ uninstall(options.env)
64
+ else:
65
+ print("Adding qdir settings to", options.env)
66
+ install(options.env)