Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Richer command output #791

Merged
merged 31 commits into from
Jan 10, 2022
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
2ef7767
Changed signal termination
germa89 Dec 20, 2021
5f84f1c
Adding CommandOutput class.
germa89 Dec 20, 2021
9dc57fc
Substituting the output in mapdl_grpc for custom class.
germa89 Dec 20, 2021
2c508bb
Merge branch 'fix/os-error-when-using-os.kill-in-unit-testing' into f…
germa89 Dec 20, 2021
28a838f
Making sure all the methods call the parent class (str)
germa89 Dec 20, 2021
18525a7
Testing simplied version
germa89 Dec 20, 2021
9c2b52f
Added unit tests
germa89 Dec 21, 2021
915444c
Added unit tests
germa89 Dec 21, 2021
652ca3d
Fixing the style
germa89 Dec 21, 2021
c8ec465
Merge branch 'feat/richer-command-output' of https://github.com/pyans…
germa89 Dec 21, 2021
9cbda1e
Fixing grammar.
germa89 Dec 21, 2021
52c3e4a
Fixing grammar.
germa89 Dec 21, 2021
1ff363e
Merge branch 'feat/richer-command-output' of https://github.com/pyans…
germa89 Dec 21, 2021
12b143f
Merge branch 'feat/richer-command-output' of https://github.com/pyans…
germa89 Dec 21, 2021
8bc2768
Add test_class unit
germa89 Dec 21, 2021
64dbd92
Using first implementation because the second fail because of the mod…
germa89 Dec 21, 2021
c110b88
Changing implementation to not overwrite __class__.
germa89 Dec 21, 2021
9fdc7f7
Fixing sphinx building by rewriting __class__ method to not be overwr…
germa89 Dec 21, 2021
53aab29
Style check.
germa89 Dec 21, 2021
d98bd79
changing the API, cmd=command, and command= full command (cmd + args)
germa89 Dec 28, 2021
4471208
UserString Implementation
germa89 Jan 3, 2022
ebcf76d
Simplification of unit test.
germa89 Jan 3, 2022
78c5fba
Simplification of unit test.
germa89 Jan 3, 2022
f9506b5
Merge
germa89 Jan 4, 2022
8cf5632
Using str as base class.
germa89 Jan 4, 2022
9c50fe9
removed unused import.
germa89 Jan 4, 2022
4cddd9c
Merge branch 'main' into feat/richer-command-output
akaszynski Jan 5, 2022
18e76dd
Apply suggestions from code review
germa89 Jan 5, 2022
572d6cb
Adding doc info.
germa89 Jan 5, 2022
3c7f815
Merge branch 'main' into feat/richer-command-output
germa89 Jan 10, 2022
d84c79b
Removed automatically return a CommandOutput object.
germa89 Jan 10, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 161 additions & 0 deletions ansys/mapdl/core/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,3 +212,164 @@ class Commands(
"""Wrapped MAPDL commands"""

pass

class CommandOutput(str):

## References:
# - https://stackoverflow.com/questions/7255655/how-to-subclass-str-in-python
# - https://docs.python.org/3/library/collections.html#userstring-objects
# - Source code of UserString

def __new__(cls, content, cmd=None):
obj = super().__new__(cls, content)
obj._cmd = cmd
return obj

def _copyobj(self, seq):
# __new__ needs type and the args.
return self.__new__(type(self), seq, self._cmd)

# Overwriting the string methods.
# I used the UserString API.
def __getitem__(self, index):
return self._copyobj(super().__getitem__(index))

def __add__(self, other):
return self._copyobj(super().__add__(other))

def __mul__(self, n):
return self._copyobj(super().__mul__(n))

__rmul__ = __mul__

def __mod__(self, args):
return self._copyobj(super().__mod__(args))

def __rmod__(self, template):
return self._copyobj(str(template).__rmod__(self))

# the following methods are overwritten and defined in alphabetical order:
def capitalize(self):
return self._copyobj(super().capitalize())

def casefold(self):
return self._copyobj(super().casefold())

def center(self, width, *args):
return self._copyobj(super().center(width, *args))

def removeprefix(self, prefix):
return self._copyobj(super().removeprefix(prefix))

def removesuffix(self, suffix):
return self._copyobj(super().removesuffix(suffix))

def expandtabs(self, tabsize=8):
return self._copyobj(super().expandtabs(tabsize))

def join(self, seq):
return self._copyobj(super().join(seq))

def ljust(self, width, *args):
return self._copyobj(super().ljust(width, *args))

def lower(self):
return self._copyobj(super().lower())

def lstrip(self, chars=None):
return self._copyobj(super().lstrip(chars))

maketrans = str.maketrans

def replace(self, old, new, maxsplit=-1):
return self._copyobj(super().replace(old, new, maxsplit))

def rjust(self, width, *args):
return self._copyobj(super().rjust(width, *args))

def rstrip(self, chars=None):
return self._copyobj(super().rstrip(chars))

def strip(self, chars=None):
return self._copyobj(super().strip(chars))

def swapcase(self):
return self._copyobj(super().swapcase())

def title(self):
return self._copyobj(super().title())

def translate(self, *args):
return self._copyobj(super().translate(*args))

def upper(self):
return self._copyobj(super().upper())

def zfill(self, width):
return self._copyobj(super().zfill(width))

def splitlines(self, keepends=False):
return [self._copyobj(each) for each in super().splitlines(keepends)]

def rpartition(self, sep):
return tuple(self._copyobj(each) for each in super().rpartition(sep))

def rstrip(self, chars=None):
return self._copyobj(super().rstrip(chars))

def split(self, sep=None, maxsplit=-1):
return [self._copyobj(each) for each in super().split(sep, maxsplit)]

def rsplit(self, sep=None, maxsplit=-1):
return [self._copyobj(each) for each in super().rsplit(sep, maxsplit)]

def format(self, *args, **kwds):
return self._copyobj(super().format(*args, **kwds))

def format_map(self, mapping):
return self._copyobj(super().format_map(mapping))

@property
def cmd(self):
return self._cmd

@cmd.setter
def cmd(self, cmd):
"""Forbidden to change the value of ``cmd``."""
pass

class CommandOutput2(str):

## References:
# - https://stackoverflow.com/questions/7255655/how-to-subclass-str-in-python
# - https://docs.python.org/3/library/collections.html#userstring-objects
# - Source code of UserString

def __new__(cls, content, cmd=None):
obj = super().__new__(cls, content)
obj._cmd = cmd
return obj

# def _copyobj(self, seq):
# # __new__ needs type and the args.
# # return self.__new__(type(self), seq, self._cmd)

def __getattribute__(self, name):
if name in dir(str) and name != '_copyobj': # only handle str methods here
def method(self, *args, **kwargs):
value = getattr(super(), name)(*args, **kwargs)
# not every string method returns a str:
if isinstance(value, str) and not isinstance(value, CommandOutput2):
# return type(self)(value)
return self.__new__(value, self._cmd)

elif isinstance(value, list):
return [self.__new__(i, self._cmd) for i in value]

elif isinstance(value, tuple):
return tuple(self.__new__(i, self._cmd) for i in value)
else: # dict, bool, or int or type
return value
return method.__get__(self) # bound method
# else: # delegate to parent
# return super().__getattribute__(name)
4 changes: 2 additions & 2 deletions ansys/mapdl/core/mapdl_grpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
)
from ansys.mapdl.core import __version__, _LOCAL_PORTS
from ansys.mapdl.core import check_version

from ansys.mapdl.core.commands import CommandOutput as CommandOutput

VOID_REQUEST = anskernel.EmptyRequest()

Expand Down Expand Up @@ -624,7 +624,7 @@ def _run(self, cmd, verbose=False, mute=None):
else:
response = self._send_command(cmd, mute=mute)
self._busy = False
return response.strip()
return CommandOutput(response.strip(), cmd)

@property
def busy(self):
Expand Down
6 changes: 5 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from pathlib import Path
from collections import namedtuple
import os
import signal
import time

import pytest
Expand Down Expand Up @@ -76,7 +77,10 @@
def check_pid(pid):
"""Check For the existence of a pid."""
try:
os.kill(pid, 0)
# There are two main options:
# - Termination signal (SIGTERM) int=15. Soft termination (Recommended)
# - Kill signal (KILLTER). int=9. Hard termination
os.kill(pid, signal.SIGTERM)
except OSError:
return False
else:
Expand Down
Loading