def __init__(self, shell_binary, shell_argstr, return_code_echo_command, command_separator, is_interactive): """Sets up the context for a system shell process. Parameters ---------- shell_binary : str The shell binary, e.g. ``/bin/bash``, to start. shell_argstr : str Additional arguments to be passed to the shell at start. return_code_echo_command : str A command string in the shell's own language that prints to standard output the return code of the previously executed command. command_separator : str The character sequence to separate commands with. is_interactive : bool Whether the started shell is an interactive one. This does only change the behaviour of the context manager, to make the shell itself interactive, additional arguments in `shell_argstr` might need to be passed. """ self._args = shell_argstr self._binary = shell_binary # Note: The execution of the command and the reading of the output # has to happen BEFORE this timeout is hit, but a large timeout would # also mean waiting a lot for small commands, so this has to be # balanced carefully. self._capture = Capture(timeout=0.5, buffer_size=-1) self._command = Command(shell_binary + ' ' + shell_argstr, stdout=self._capture) self._echo = return_code_echo_command + command_separator self._interactive = is_interactive self._separator = command_separator self._started = False
def startStreamer(self): if not self._process: command = [ "/mjpeg_streamer/mjpg_streamer", "-i", "input_uvc.so -d %s -f %s -r %s --no_dynctrl%s" % \ (self._device, self._fps, self._size, ' -y' if self._format == 'x-raw' else '' ), "-o", "output_http.so -p %d" % self._httpPort ] self._process = Command( command, env={'LD_LIBRARY_PATH': '/mjpeg_streamer'}, stderr=open(os.devnull, 'w') ) if self._process: self._process.run(async=True) time.sleep(0.2) running = self._process.returncode is None return running return False
def test_command_splitting(self): logger.debug('test_command started') cmd = 'echo foo' c = Command(cmd) self.assertEqual(c.args, cmd.split()) c = Command(cmd, shell=True) self.assertEqual(c.args, cmd)
def get_metadata(self, image): docker_command = str(self.location) + ' inspect ' + image print docker_command p = Command(docker_command, stdout=Capture(buffer_size=-1)) p.run() # Testing directly in the string works if the output is only # one line. # if 'No such image' in p.stdout: # raise DockerImageError # data = [json.loads(str(item)) for item in p.stdout.readline().strip().split('\n')] json_block = [] line = p.stdout.readline() while(True): if not line: break if 'no such image' in line: raise DockerImageError # Stupid sarge appears to add a blank line between # json statements. This checks for a blank line and # cycles to the next line if it is blank. if re.match(r'^\s*$', line): line = p.stdout.readline() continue json_block.append(line) line = p.stdout.readline() s = ''.join(json_block) self.metadata = json.loads(s)
def get_metadata(self): docker_command = str(self.location) + ' inspect ' + self.imageID p = Command(docker_command, stdout=Capture(buffer_size=-1)) p.run() # Testing directly in the string works if the output is only # one line. # if 'No such image' in p.stdout: # raise DockerImageError # data = [json.loads(str(item)) for item in p.stdout.readline().strip().split('\n')] json_block = [] line = p.stdout.readline() while (line): if 'no such image' in line: raise DockerImageError # Stupid sarge appears to add a blank line between # json statements. This checks for a blank line and # cycles to the next line if it is blank. if re.match(r'^\s*$', line): line = p.stdout.readline() continue json_block.append(line) line = p.stdout.readline() s = ''.join(json_block) s = s[1:-2] self.metadata = s
def remove_image(self, name): """ removes a named image :param name: the name of the image to be removed :return: none """ rm_container_string = str(self.location) + ' rm ' + name rm_container = Command(rm_container_string) rm_container.run()
def stop_container(self, name): """ stops a specified container :param name: name of the container to stop :return:none """ stop_container_string = str(self.location) + ' stop ' + name stop_container = Command(stop_container_string) stop_container.run()
def put_label_image(self, label, imageID): """put_label attaches json metadata to smartcontainer label""" # Due to the structure of docker, we have to do this in a series of # steps. This method attaches a label to a image by creating a # temporary container with a label using docker run. We then save that # container to a new image with the same. # First get tag and name for image id passed to method repository, tag = self.get_image_info(imageID) # Now "attach" the label by running a new container from the imageID label_cmd_string = str(self.location) + ' run --name = labeltmp --label=' + \ self.label_prefix + '=' + label + ' ' + imageID + ' /bin/echo' print label_cmd_string p = Command(label_cmd_string) p.run(async=True) # Save container with new label to new image with previous repo and tag commit_cmd_string = str(self.location) + ' commit labeltemp ' + repository + ':' + tag commit = Command(commit_cmd_string) commit.run() # remove temporary container rm_container_string = str(self.location) + ' rm labeltemp' rm_container = Command(rm_container_string) rm_container.run()
def container_save_as(self, name, saveas, tag): """ Saves a container to a new image :param name: the name of the container to be saved :param saveas: the name of the image to be created :param tag: the tag attached to the new image :return: none """ commit_cmd_string = str(self.location) + ' commit ' + name + ' ' + saveas if tag != "": commit_cmd_string = commit_cmd_string + ':' + tag commit = Command(commit_cmd_string) commit.run()
def container_save_as(self, name, saveas, tag): """ Saves a container to a new image :param name: the name of the container to be saved :param saveas: the name of the image to be created :param tag: the tag attached to the new image :return: none """ commit_cmd_string = str( self.location) + ' commit ' + name + ' ' + saveas if tag != "": commit_cmd_string = commit_cmd_string + ':' + tag commit = Command(commit_cmd_string) commit.run()
def test_env_usage(self): if os.name == 'nt': cmd = 'echo %FOO%' else: cmd = 'echo $FOO' if PY3: env = {'FOO': 'BAR'} else: # Python 2.x wants native strings, at least on Windows # (and literals are Unicode in this module) env = { b'FOO': b'BAR' } c = Command(cmd, env=env, stdout=Capture(), shell=True) c.run() self.assertEqual(c.stdout.text.strip(), 'BAR')
def test_env_usage(self): if os.name == 'nt': cmd = 'echo %FOO%' else: cmd = 'echo $FOO' if PY3: env = {'FOO': 'BAR'} else: # Python 2.x wants native strings, at least on Windows # (and literals are Unicode in this module) env = {b'FOO': b'BAR'} c = Command(cmd, env=env, stdout=Capture(), shell=True) c.run() self.assertEqual(c.stdout.text.strip(), 'BAR')
def test_env_usage(self): if os.name == "nt": cmd = "echo %FOO%" else: cmd = "echo $FOO" if PY3: env = {"FOO": "BAR"} else: # Python 2.x wants native strings, at least on Windows # (and literals are Unicode in this module) env = {b"FOO": b"BAR"} c = Command(cmd, env=env, stdout=Capture(), shell=True) c.run() self.assertEqual(c.stdout.text.strip(), "BAR")
def test_command_nonexistent(self): c = Command('nonesuch foo'.split(), stdout=Capture()) if PY3: ARR = self.assertRaisesRegex else: ARR = self.assertRaisesRegexp ARR(ValueError, 'Command not found: nonesuch', c.run)
def do_command(self): """do_command is main entry point for capturing docker commands""" # First run the command and capture the output. # For efficiency this should probably change such that # if a command doesn't have a capture handler we execute # the command uncaptured. Most commands are going to be captured # for provenance, so this efficiency concern is probably moot. if self.location is None: self.find_docker() cmd_string = str(self.location) + ' ' + self.command p = Command(cmd_string) p.run(async=True) for name in snarf_docker_commands: if name in self.command: self.capture_cmd_workflow()
def test_env(self): e = os.environ if PY3: env = {'FOO': 'BAR'} else: # Python 2.x wants native strings, at least on Windows # (and literals are Unicode in this module) env = {b'FOO': b'BAR'} c = Command('echo foo', env=env) d = c.kwargs['env'] ek = set(e) dk = set(d) ek.add('FOO') self.assertEqual(dk, ek) self.assertEqual(d['FOO'], 'BAR')
def startVideo(self): if not self._process: command = [ "/mjpeg_streamer/mjpg_streamer", "-i", "input_uvc.so -d %s -f %s -r %s --no_dynctrl%s" % (self._device, self._fps, self._size, ' -y' if self._format == 'x-raw' else ''), "-o", "output_http.so -p %d" % self._httpPort ] self._process = Command(command, env={'LD_LIBRARY_PATH': '/mjpeg_streamer'}, stderr=open(os.devnull, 'w')) if self._process: self._process.run(async=True) time.sleep(0.2) return self._process.returncode is None return False
def fileCopyIn(self, dockerlocation, containerid, filename, path): copy_cmd = str( dockerlocation ) + ' cp ' + filename + " " + containerid + ":" + path + filename thisCommand = Command(copy_cmd) thisCommand.run()
def test_command_run(self): c = Command('echo foo'.split(), stdout=Capture()) c.run() self.assertEqual(c.returncode, 0)
class Shell(AbstractContextManager): """A real shell running on the host machine, to be used in a context.""" def __init__(self, shell_binary, shell_argstr, return_code_echo_command, command_separator, is_interactive): """Sets up the context for a system shell process. Parameters ---------- shell_binary : str The shell binary, e.g. ``/bin/bash``, to start. shell_argstr : str Additional arguments to be passed to the shell at start. return_code_echo_command : str A command string in the shell's own language that prints to standard output the return code of the previously executed command. command_separator : str The character sequence to separate commands with. is_interactive : bool Whether the started shell is an interactive one. This does only change the behaviour of the context manager, to make the shell itself interactive, additional arguments in `shell_argstr` might need to be passed. """ self._args = shell_argstr self._binary = shell_binary # Note: The execution of the command and the reading of the output # has to happen BEFORE this timeout is hit, but a large timeout would # also mean waiting a lot for small commands, so this has to be # balanced carefully. self._capture = Capture(timeout=0.5, buffer_size=-1) self._command = Command(shell_binary + ' ' + shell_argstr, stdout=self._capture) self._echo = return_code_echo_command + command_separator self._interactive = is_interactive self._separator = command_separator self._started = False def __enter__(self): """Starts the shell in a context manager setting.""" return self.start() def __exit__(self, exc_type, exc_value, trace): """Destroys the shell and leaves the context.""" if self._interactive: self.kill() else: self.terminate() return False def start(self): """Starts the underlying shell process as configured in :py:func:`__init__`. """ if self._started: raise OSError(EEXIST, "The shell is already running!") print("[Shell] Starting '{0} {1}'...".format(self._binary, self._args), file=sys.stderr) try: self._command.run(input=PIPE, async_=True) except ValueError: raise FileNotFoundError("The shell binary '{0}' cannot be found " "on the system.".format(self._binary)) self._started = True return self @property def pid(self): """The PID of the started process.""" if not self._started: raise OSError(ESRCH, "The shell is not running!") return self._command.process.pid def kill(self): """Kills (by sending ``SIGKILL`` (``9``)) to the shell process.""" if not self._started: raise OSError(ESRCH, "The shell is not running!") self._command.kill() self._command.wait() self._capture.close(True) self._started = False def terminate(self): """Terminates (by sending ``SIGTERM`` (``15``)) to the shell process. Note ---- Interactive shells (:py:attr:`is_interactive`) usually catch and ignore this signal, and as such, :py:func:`kill` should be used to shut them down properly. """ if not self._started: raise OSError(ESRCH, "The shell is not running!") self._command.terminate() self._command.wait() self._capture.close(True) self._started = False def execute_command(self, cmd, timeout=None): """Execute `cmd` in the shell, wait `timeout` seconds, and read back the result. Parameters ---------- cmd : str The command to execute. This string will be written into the shell's standard input verbatim. timeout : int The time (in seconds) to wait before the output of the command is read. Returns ------- return_code : int The return code of the executed command. result : str The *standard output* of the executed command. Note ---- The command executed in the shell is extended with :py:attr:`command_separator` and :py:attr:`return_code_echo_command`, and written to the shell. In case of a conventional ``/bin/bash`` shell, for example, executing `cmd` ``echo "Foo"`` will actually execute: .. code-block:: bash echo "Foo"; echo $; Which will result in the output: .. code-block:: bash Foo 0 to be read as a result. Warning ------- The underlying library and the execution of piped shells does not allow a good method of "sensing" when the output became available while keeping interactivity. A too small `timeout` on a loaded system might result in output being lost, while a too big one will result in every command waiting for a considerable time. """ cmd = cmd + self._separator + self._echo print("[Shell '{0}'] Running command:\n{1}\n".format( self._binary, '\n'.join(list(map(lambda l: " > " + l, cmd.split('\n'))))), file=sys.stderr) self._command.stdin.write(cmd.encode('utf-8')) self._command.stdin.flush() stdout = self._capture.read(timeout=timeout) parts = stdout.decode().rstrip().split('\n') result, returncode = '\n'.join(parts[:-1]).rstrip(), parts[-1].rstrip() print("[Shell '{0}'] Command result #{1}:\n{2}".format( self._binary, returncode, result), file=sys.stderr) return int(returncode), result
class MJPEGStreamer(object): _httpPort = 8085 def __init__(self, videoDevice, size, fps, format): self._logger = logging.getLogger(__name__) self._device = '/dev/video%d' % videoDevice self._size = size self._fps = fps self._format = format self._videoRunning = False self._process = None self._streaming = False self._needsExposure = True self._infoArea = cv2.imread( os.path.join(app.static_folder, 'img', 'camera-info-overlay.jpg'), cv2.cv.CV_LOAD_IMAGE_COLOR) self._infoAreaShape = self._infoArea.shape #precalculated stuff watermark = cv2.imread( os.path.join(app.static_folder, 'img', 'astroprint_logo.png')) watermark = cv2.resize( watermark, (100, 100 * watermark.shape[0] / watermark.shape[1])) self._watermarkShape = watermark.shape watermarkMask = cv2.cvtColor(watermark, cv2.COLOR_BGR2GRAY) / 255.0 watermarkMask = np.repeat(watermarkMask, 3).reshape( (self._watermarkShape[0], self._watermarkShape[1], 3)) self._watermakMaskWeighted = watermarkMask * watermark self._watermarkInverted = 1.0 - watermarkMask def startStreamer(self): if not self._process: command = [ "/mjpeg_streamer/mjpg_streamer", "-i", "input_uvc.so -d %s -f %s -r %s --no_dynctrl%s" % (self._device, self._fps, self._size, ' -y' if self._format == 'x-raw' else ''), "-o", "output_http.so -p %d" % self._httpPort ] self._process = Command(command, env={'LD_LIBRARY_PATH': '/mjpeg_streamer'}, stderr=open(os.devnull, 'w')) if self._process: self._process.run(async=True) time.sleep(0.2) running = self._process.returncode is None return running return False def startVideo(self): if self._streaming: return True if self.startStreamer(): self._streaming = True return True return False def stop(self): if self._process: if self._process.returncode is None: self._process.terminate() tries = 4 while self._process.returncode is None: if tries > 0: tries -= 1 time.sleep(0.5) self._process.poll() else: break if self._process.returncode is None: self._logger.warn( 'Unable to terminate nicely, killing the process.') self._process.kill() self._process.wait() self._process = None self._streaming = False self._needsExposure = True def stopVideo(self): self._streaming = False def isVideoStreaming(self): return self._streaming def getPhoto(self, doneCb, text=None): image = None if not self._process: if not self.startStreamer(): self._logger.error('Unable to start MJPEG Streamer') doneCb(None) return try: if self._needsExposure and not self.isVideoStreaming(): time.sleep( 1.8 ) # we need to give the camera some time to stabilize the image. 1.8 secs has been tested to work in low end cameras self._needsExposure = False response = urllib2.urlopen('http://127.0.0.1:%d?action=snapshot' % self._httpPort) image = response.read() except urllib2.URLError as e: self._logger.error(e) if image and text: decodedImage = cv2.imdecode(np.fromstring(image, np.uint8), cv2.CV_LOAD_IMAGE_COLOR) self._apply_watermark(decodedImage, text) image = cv2.cv.EncodeImage( '.jpeg', cv2.cv.fromarray(decodedImage), [cv2.cv.CV_IMWRITE_JPEG_QUALITY, 80]).tostring() doneCb(image) def _apply_watermark(self, img, text): if text and img != None: imgPortion = img[-(self._watermarkShape[0] + 5):-5, -(self._watermarkShape[1] + 5):-5] img[-(self._watermarkShape[0] + 5):-5, -(self._watermarkShape[1] + 5):-5] = (self._watermarkInverted * imgPortion) + self._watermakMaskWeighted img[:self._infoAreaShape[0], :self. _infoAreaShape[1]] = self._infoArea cv2.putText(img, text, (30, 17), cv2.FONT_HERSHEY_PLAIN, 1.0, (81, 82, 241), thickness=1) return True return False
class MJPEGStreamer(object): _httpPort = 8085 def __init__(self, videoDevice, size, fps, format): self._device = '/dev/video%d' % videoDevice self._size = size self._fps = fps self._format = format self._videoRunning = False self._process = None self._infoArea = cv2.imread(os.path.join(app.static_folder, 'img', 'camera-info-overlay.jpg'), cv2.cv.CV_LOAD_IMAGE_COLOR) self._infoAreaShape = self._infoArea.shape #precalculated stuff watermark = cv2.imread(os.path.join(app.static_folder, 'img', 'astroprint_logo.png')) watermark = cv2.resize( watermark, ( 100, 100 * watermark.shape[0]/watermark.shape[1] ) ) self._watermarkShape = watermark.shape watermarkMask = cv2.cvtColor(watermark, cv2.COLOR_BGR2GRAY) / 255.0 watermarkMask = np.repeat( watermarkMask, 3).reshape( (self._watermarkShape[0],self._watermarkShape[1],3) ) self._watermakMaskWeighted = watermarkMask * watermark self._watermarkInverted = 1.0 - watermarkMask def startVideo(self): if not self._process: command = [ "/mjpeg_streamer/mjpg_streamer", "-i", "input_uvc.so -d %s -f %s -r %s --no_dynctrl%s" % (self._device, self._fps, self._size, ' -y' if self._format == 'x-raw' else ''), "-o", "output_http.so -p %d" % self._httpPort ] self._process = Command(command, env={'LD_LIBRARY_PATH': '/mjpeg_streamer'}, stderr=open(os.devnull, 'w')) if self._process: self._process.run(async=True) time.sleep(0.2) return self._process.returncode is None return False def stopVideo(self): if self._process: if self._process.returncode is None: self._process.terminate() self._process.wait() self._process = None def getPhoto(self, text=None, doneCb=None): image = None stopAfterPhoto = False if not self._process: self.startVideo() stopAfterPhoto = True try: if self._format == 'x-raw': time.sleep(1.8) # we need to give the camera some time to stabilize the image. 1.8 secs has been tested to work in low end cameras response = urllib2.urlopen('http://127.0.0.1:%d?action=snapshot' % self._httpPort) image = response.read() except urllib2.URLError: pass if image and text: decodedImage = cv2.imdecode(np.fromstring(image, np.uint8), cv2.CV_LOAD_IMAGE_COLOR) self._apply_watermark(decodedImage, text) image = cv2.cv.EncodeImage('.jpeg', cv2.cv.fromarray(decodedImage), [cv2.cv.CV_IMWRITE_JPEG_QUALITY, 80]).tostring() if stopAfterPhoto: self.stopVideo() if doneCb: doneCb(image) else: return image def _apply_watermark(self, img, text): if text and img != None: imgPortion = img[-(self._watermarkShape[0]+5):-5, -(self._watermarkShape[1]+5):-5] img[-(self._watermarkShape[0]+5):-5, -(self._watermarkShape[1]+5):-5] = (self._watermarkInverted * imgPortion) + self._watermakMaskWeighted img[:self._infoAreaShape[0], :self._infoAreaShape[1]] = self._infoArea cv2.putText(img, text, (30,17), cv2.FONT_HERSHEY_PLAIN, 1.0, (81,82,241), thickness=1) return True return False
def __init__(self, structure): """ Args: """ self.input_structure = structure.copy() iso_location = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'iso/') self.input_filename_cif = os.path.join(iso_location, 'findsym_input.mcif') self.output_filename_cif = os.path.join(iso_location, 'findsym.cif') # self.input_filename_findsym = os.path.join(iso_location, 'findsym_in.in') self.input_filename_findsym = 'findsym_in.in' self.write_cif_input() # write initial input from mcif file findsym_command = 'findsym_cifinput ' + self.input_filename_cif logger.debug("""starting isotropy session in {} using isotropy in: {}""".format( os.getcwd(), iso_location)) self.findsym_cifinput_process = Command(os.path.join( iso_location, findsym_command), stdout=Capture(buffer_size=1), env={"ISODATA": iso_location}) try: self.findsym_cifinput_process.run(input=PIPE, async_=False) except FileNotFoundError: raise Exception( "Couldn't find Isotropy (findsym) for Linux, see installation instructions" ) with open(self.input_filename_findsym, "w") as io_file: # move past initial output keep_reading = True while keep_reading: # this_line = self.iso_process.stdout.readline().decode() this_line = self.findsym_cifinput_process.stdout.readline( ).decode() if this_line: # don't log until isotropy responds logger.debug("isotropy: {}".format(this_line)) io_file.write(this_line) else: keep_reading = False # run findsym findsym_command = 'findsym ' + self.input_filename_findsym self.findsym_process = Command(os.path.join(iso_location, findsym_command), stdout=Capture(buffer_size=1), env={"ISODATA": iso_location}) try: self.findsym_process.run(input=PIPE, async_=False) except FileNotFoundError: raise Exception( "Couldn't find Isotropy (findsym) for Linux, see installation instructions" ) # # move past initial output # keep_reading = True # while keep_reading: # this_line = self.findsym_process.stdout.readline().decode() # # this_line = self.read_iso_line() # if this_line: # don't log until isotropy responds # logger.debug("isotropy: {}".format(this_line)) # else: # keep_reading = False self.output_cif_file = CifFile.from_file(self.output_filename_cif) msg_int_symbol = int(self.output_cif_file.data['findsym-output']. data['_symmetry_Int_Tables_number']) self.magnetic_space_group = MagneticSpaceGroup(msg_int_symbol)
class FindsymSession: """ """ def __init__(self, structure): """ Args: """ self.input_structure = structure.copy() iso_location = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'iso/') self.input_filename_cif = os.path.join(iso_location, 'findsym_input.mcif') self.output_filename_cif = os.path.join(iso_location, 'findsym.cif') # self.input_filename_findsym = os.path.join(iso_location, 'findsym_in.in') self.input_filename_findsym = 'findsym_in.in' self.write_cif_input() # write initial input from mcif file findsym_command = 'findsym_cifinput ' + self.input_filename_cif logger.debug("""starting isotropy session in {} using isotropy in: {}""".format( os.getcwd(), iso_location)) self.findsym_cifinput_process = Command(os.path.join( iso_location, findsym_command), stdout=Capture(buffer_size=1), env={"ISODATA": iso_location}) try: self.findsym_cifinput_process.run(input=PIPE, async_=False) except FileNotFoundError: raise Exception( "Couldn't find Isotropy (findsym) for Linux, see installation instructions" ) with open(self.input_filename_findsym, "w") as io_file: # move past initial output keep_reading = True while keep_reading: # this_line = self.iso_process.stdout.readline().decode() this_line = self.findsym_cifinput_process.stdout.readline( ).decode() if this_line: # don't log until isotropy responds logger.debug("isotropy: {}".format(this_line)) io_file.write(this_line) else: keep_reading = False # run findsym findsym_command = 'findsym ' + self.input_filename_findsym self.findsym_process = Command(os.path.join(iso_location, findsym_command), stdout=Capture(buffer_size=1), env={"ISODATA": iso_location}) try: self.findsym_process.run(input=PIPE, async_=False) except FileNotFoundError: raise Exception( "Couldn't find Isotropy (findsym) for Linux, see installation instructions" ) # # move past initial output # keep_reading = True # while keep_reading: # this_line = self.findsym_process.stdout.readline().decode() # # this_line = self.read_iso_line() # if this_line: # don't log until isotropy responds # logger.debug("isotropy: {}".format(this_line)) # else: # keep_reading = False self.output_cif_file = CifFile.from_file(self.output_filename_cif) msg_int_symbol = int(self.output_cif_file.data['findsym-output']. data['_symmetry_Int_Tables_number']) self.magnetic_space_group = MagneticSpaceGroup(msg_int_symbol) def get_msg(self): return self.magnetic_space_group def __enter__(self): return self def __exit__(self, exec_type, exc_value, exc_traceback): self.sendCommand("QUIT") def sendCommand(self, command): # read the '*' that indicates the prompt so they don't build up this_line = self.read_iso_line() # logger.debug("reading *: {}".format(this_line)) logger.debug(f'python: {command}') # self.findsym_process.stdin.write(bytes(command + "\n", "ascii")) # self.findsym_process.stdin.flush() def write_cif_input(self): cif_writer = CifWriter(struct=self.input_structure, symprec=None, write_magmoms=True, significant_figures=8, angle_tolerance=5.0, refine_struct=True) cif_writer.write_file(self.input_filename_cif) # def write_input(self): # inputfile_string = """ \ # !useKeyWords # !title # Structure input file # !latticeParameters # %s # !unitCellCentering # %s # !atomCount # %s # !atomPosition # %s # """ % (lat_param_s, unit_cell_centering_s, # atom_count_s, atom_pos_s) def read_iso_line(self): raw = self.findsym_process.stdout.readline().decode() this_line = raw.rstrip('\n') logger.debug("isotropy: {}".format(this_line)) return this_line
def __init__(self, values=None, shows=None, labels=None, setting=None): """ Args: values: dictionary of keys to be set to values key is a string specifying what is being set e.g. "basis", "cell", "irrep", "kpoint", "parent" value is what this key is being set to shows: A list of strings corresponding to data which will be returned when display is run. Note: some show commands accept additional parameters, for now these must be included in the string. Eventually parsing of display output should be good enough that they are not needed. labels: NOT YET IMPLEMENTED dictionary where the key corresponds to the object whose notation is being altered, the value corresponds to the new notation to be used for this object for example {"spacegroup": "SCHOENFLIES"} will cause returned results and entered values to use schoenflies notation setting: a string or list of strings to be passed to the setting command can be used to change settings, origin, unique axis and/or cell choice. Can also specify if magnetic spacegroups are desired for now the setting options can only be set when creating an Isotropy object, not changed later """ iso_location = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'iso/') logger.debug("""starting isotropy session in {} using isotropy in: {}""".format( os.getcwd(), iso_location)) self.iso_process = Command(os.path.join(iso_location, 'iso'), stdout=Capture(buffer_size=1), env={"ISODATA": iso_location}) try: self.iso_process.run(input=PIPE, async_=True) except FileNotFoundError: raise Exception( "Couldn't find Isotropy for Linux, see installation instructions" ) # move past initial output keep_reading = True while keep_reading: # this_line = self.iso_process.stdout.readline().decode() this_line = self.read_iso_line() if this_line: # don't log until isotropy responds logger.debug("isotropy: {}".format(this_line)) if this_line == 'Use "VALUE IRREP VERSION" to change version': keep_reading = False self.screen = 999 # exploit this too make parsing output easier? self.sendCommand("SCREEN {}".format(self.screen)) #self.page = "NOBREAK" # still feels the need to periodicly put in labels self.page = "999" self.sendCommand("PAGE {}".format(self.page)) if setting: if type(setting) == list: self.setting = setting else: self.setting = [setting] else: self.setting = ["INTERNATIONAL"] for s in self.setting: self.sendCommand("SETTING {}".format(s)) self.values = Values(self, values) self.shows = Shows(self, shows)
class IsotropySession: """ Make simple requests to isotropy. isotropy session is kept running in background until closed should be used with 'with' statements to ensure isotropy is exited properly ex: with IsotropySession() as isos: do things with isos """ def __init__(self, values=None, shows=None, labels=None, setting=None): """ Args: values: dictionary of keys to be set to values key is a string specifying what is being set e.g. "basis", "cell", "irrep", "kpoint", "parent" value is what this key is being set to shows: A list of strings corresponding to data which will be returned when display is run. Note: some show commands accept additional parameters, for now these must be included in the string. Eventually parsing of display output should be good enough that they are not needed. labels: NOT YET IMPLEMENTED dictionary where the key corresponds to the object whose notation is being altered, the value corresponds to the new notation to be used for this object for example {"spacegroup": "SCHOENFLIES"} will cause returned results and entered values to use schoenflies notation setting: a string or list of strings to be passed to the setting command can be used to change settings, origin, unique axis and/or cell choice. Can also specify if magnetic spacegroups are desired for now the setting options can only be set when creating an Isotropy object, not changed later """ iso_location = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'iso/') logger.debug("""starting isotropy session in {} using isotropy in: {}""".format( os.getcwd(), iso_location)) self.iso_process = Command(os.path.join(iso_location, 'iso'), stdout=Capture(buffer_size=1), env={"ISODATA": iso_location}) try: self.iso_process.run(input=PIPE, async_=True) except FileNotFoundError: raise Exception( "Couldn't find Isotropy for Linux, see installation instructions" ) # move past initial output keep_reading = True while keep_reading: # this_line = self.iso_process.stdout.readline().decode() this_line = self.read_iso_line() if this_line: # don't log until isotropy responds logger.debug("isotropy: {}".format(this_line)) if this_line == 'Use "VALUE IRREP VERSION" to change version': keep_reading = False self.screen = 999 # exploit this too make parsing output easier? self.sendCommand("SCREEN {}".format(self.screen)) #self.page = "NOBREAK" # still feels the need to periodicly put in labels self.page = "999" self.sendCommand("PAGE {}".format(self.page)) if setting: if type(setting) == list: self.setting = setting else: self.setting = [setting] else: self.setting = ["INTERNATIONAL"] for s in self.setting: self.sendCommand("SETTING {}".format(s)) self.values = Values(self, values) self.shows = Shows(self, shows) def __enter__(self): return self def __exit__(self, exec_type, exc_value, exc_traceback): self.sendCommand("QUIT") def restart_session(self): try: self.sendCommand("QUIT") except BrokenPipeError: logger.debug('Ignoring BrokenPipeError on restart') self.iso_process.kill() files_to_remove = glob("*.iso") logger.warning("removing iso db files {}".format(files_to_remove)) for f in files_to_remove: os.remove(f) self.__init__( values=self.values, shows=self.shows, labels=None, setting=self.setting) # TODO: update if labels implemented def sendCommand(self, command): # read the '*' that indicates the prompt so they don't build up this_line = self.read_iso_line() # logger.debug("reading *: {}".format(this_line)) logger.debug(f'python: {command}') self.iso_process.stdin.write(bytes(command + "\n", "ascii")) self.iso_process.stdin.flush() def getDisplayData(self, display, raw=False, delay=None): """ Args: display: what we are asking isotropy to display the command sent will be 'DISPLAY {display}' raw: if true return a string of the raw output from isotropy otherwise the output is automaticly parsed in to a list of dictionaries """ self.sendCommand("DISPLAY {}".format(display)) # really annoying, TODO: should find a way to not need this delay ever if delay is not None: time.sleep(delay) lines = [] keep_reading = True while keep_reading: this_line = self.read_iso_line() if this_line in ['*', '']: # if there is no output '' is returned above keep_reading = False elif re.match(".*You have requested information about .*", this_line): self.read_iso_line() # read past irrep:... self.read_iso_line() # read past The data base for these... self.read_iso_line() # read past Should this... self.read_iso_line() # read past Enter RETURN self.sendCommand("") self.read_iso_line() # read past Adding for i in range(10): possibly_blank = self.read_iso_line() if not (possibly_blank in [ '*', '' ]): # if there is no output '' is returned above logger.debug( "moved past data base prompt, adding results") lines.append(possibly_blank) break else: if i == 9: logger.debug( "moved past data base prompt, no results") elif re.match(".*Data base for these coupled subgroups .*", this_line): self.read_iso_line() # read past Should this... self.read_iso_line() # read past Enter RETURN self.sendCommand("") self.read_iso_line() # read past Adding for i in range(10): possibly_blank = self.read_iso_line() if not (possibly_blank in [ '*', '' ]): # if there is no output '' is returned above logger.debug( "moved past data base prompt, adding results") lines.append(possibly_blank) break else: if i == 9: logger.debug( "moved past data base prompt, no results") else: lines.append(this_line) if not raw: return self._parse_output(lines) return lines def read_iso_line(self): raw = self.iso_process.stdout.readline().decode() this_line = raw.rstrip('\n') logger.debug("isotropy: {}".format(this_line)) if re.match('.*program\shas\sbombed.*', this_line): raise IsotropyBombedException() if re.match(".*Basis\svectors\sare\snot\sa\sright\-handed\sset.*", this_line): raise IsotropyBasisException() if re.match( ".*not\sall\selements\sof\sthe\ssubgroup\sare\selements\sof\sparent\sgroup.*", this_line): raise IsotropySubgroupException() return this_line def _parse_output(self, lines): indexes = detect_column_indexes(lines) split_by_ind = [split_line_by_indexes(indexes, line) for line in lines] parsed_output = [{ key: detect_data_form_and_convert(prop) for key, prop in result.items() } for result in detect_multirows_and_split(split_by_ind)] return parsed_output
def fileCopyIn(self, dockerlocation, containerid, filename, path): copy_cmd = str(dockerlocation) + ' cp '+ filename + " " + containerid + ":" + path + filename thisCommand = Command(copy_cmd) thisCommand.run()