From b927226884a2b64f109f9d690f0849f3f8921997 Mon Sep 17 00:00:00 2001 From: Ugur Yilmaz Date: Wed, 10 Jun 2020 09:14:27 +0000 Subject: [PATCH 1/2] revert doc generation --- .readthedocs.yml | 2 - docs/src/conf.py | 13 +-- docs/src/environment.yml | 10 --- docs/src/mock_tango_extension.py | 133 ------------------------------- 4 files changed, 2 insertions(+), 156 deletions(-) delete mode 100644 .readthedocs.yml delete mode 100644 docs/src/environment.yml delete mode 100644 docs/src/mock_tango_extension.py diff --git a/.readthedocs.yml b/.readthedocs.yml deleted file mode 100644 index 9e43bc0..0000000 --- a/.readthedocs.yml +++ /dev/null @@ -1,2 +0,0 @@ -conda: - file: docs/src/environment.yml diff --git a/docs/src/conf.py b/docs/src/conf.py index 5584c81..e2f9a5d 100644 --- a/docs/src/conf.py +++ b/docs/src/conf.py @@ -19,18 +19,9 @@ import sys sys.path.append(os.path.abspath('../../csp-lmc-common/')) sys.path.append(os.path.abspath('../../csp-lmc-mid/')) -sys.path.append(os.path.abspath('./')) -# Import tango -try: - import tango -except ImportError: - from mock_tango_extension import tango -from tango import Release -print("Building documentation for PyTango {0}".format(Release.version_long)) -print("Using PyTango from: {0}".format(os.path.dirname(tango.__file__))) - +#import tango #import skabase -autodoc_mock_imports = ['PyTango','run', 'DeviceMeta', 'command', +autodoc_mock_imports = ['PyTango', 'tango', 'tango-server','run', 'DeviceMeta', 'command', 'future', 'future.utils', 'logging', 'logging.handlers', 'ska', 'ska.base', 'SKAMaster', 'SKASubarray','numpy' ] diff --git a/docs/src/environment.yml b/docs/src/environment.yml deleted file mode 100644 index 4598327..0000000 --- a/docs/src/environment.yml +++ /dev/null @@ -1,10 +0,0 @@ -name: py3 -dependencies: -- python=3 -- numpy -- gevent -- graphviz -- mock -- pillow -- sphinx -- sphinx_rtd_theme diff --git a/docs/src/mock_tango_extension.py b/docs/src/mock_tango_extension.py deleted file mode 100644 index 6197d2b..0000000 --- a/docs/src/mock_tango_extension.py +++ /dev/null @@ -1,133 +0,0 @@ -"""Mock the tango._tango extension module. - -This is useful to build the documentation without building the extension. -However this is a bit tricky since the python side relies on what the -extension exposes. Here is the list of the mocking aspects that require -special attention: - - - __doc__ should not contain the mock documentation - - __mro__ is required for autodoc - - __name__ attribute is required - - Device_6Impl class should not be accessible - - the __base__ attribute for Device_[X]Impl is required - - it shoud be possible to set __init__, __getattr__ and __setattr__ methods - - tango.base_types.__document_enum needs to be patched before it is called - - tango.base_types.__document_method needs to be patched before it is called - - the mocks should not have any public methods such as assert_[...] - - _tango.constants.TgLibVers is required (e.g. '9.2.2') - - _tango._get_tango_lib_release function is required (e.g. lambda: 922) - - tango._tango AND tango.constants modules have to be patched - - autodoc requires a proper inheritance for the device impl classes - -Patching tango._tango using sys.modules does not seem to work for python -version older than 3.5 (failed with 2.7 and 3.4) -""" - -# Imports -import sys -from unittest.mock import MagicMock - -__all__ = ('tango',) - - -# Constants -TANGO_VERSION = '9.2.2' -TANGO_VERSION_INT = int(TANGO_VERSION[::2]) - - -# Extension mock class -class ExtensionMock(MagicMock): - - # Remove the mock documentation - __doc__ = None - - # The method resolution order is required for autodoc - __mro__ = object, - - @property - def __name__(self): - # __name__ is used for some objects - if self._mock_name is None: - return '' - return self._mock_name.split('.')[-1] - - def __getattr__(self, name): - # Limit device class discovery - if name == 'Device_6Impl': - raise AttributeError - # Regular mock behavior - return MagicMock.__getattr__(self, name) - - def __setattr__(self, name, value): - # Ignore unsupported magic methods - if name in ["__init__", "__getattr__", "__setattr__", - "__str__", "__repr__"]: - return - # Hook as soon as possible and patch the documentation methods - if name == 'side_effect' and self.__name__ == 'AccessControlType': - import tango.utils - import tango.base_types - import tango.device_server - import tango.connection - tango.utils.__dict__['document_enum'] = document_enum - tango.utils.__dict__['document_method'] = document_method - tango.base_types.__dict__['__document_enum'] = document_enum - tango.device_server.__dict__['__document_method'] = document_method - tango.connection.__dict__['__document_method'] = document_method - tango.connection.__dict__['__document_static_method'] = document_method - MagicMock.__setattr__(self, name, value) - - -# Remove all public methods -for name in dir(ExtensionMock): - if not name.startswith('_') and \ - callable(getattr(ExtensionMock, name)): - setattr(ExtensionMock, name, None) - - -# Patched version of document_enum -def document_enum(klass, enum_name, desc, append=True): - getattr(klass, enum_name).__doc__ = desc - - -# Patched version of document_enum -def document_method(klass, name, doc, add=True): - method = lambda self: None - method.__doc__ = doc - method.__name__ = name - setattr(klass, name, method) - - -# Use empty classes for device impl inheritance scheme -def set_device_implementations(module): - attrs = {'__module__': module.__name__} - module.DeviceImpl = type('DeviceImpl', (object,), attrs) - module.Device_2Impl = type('Device_2Impl', (module.DeviceImpl,), attrs) - module.Device_3Impl = type('Device_3Impl', (module.Device_2Impl,), attrs) - module.Device_4Impl = type('Device_4Impl', (module.Device_3Impl,), attrs) - module.Device_5Impl = type('Device_5Impl', (module.Device_4Impl,), attrs) - - -# Use empty classes for device proxy inheritance scheme -def set_device_proxy_implementations(module): - attrs = {'__module__': module.__name__} - module.Connection = type('Connection', (object,), attrs) - module.DeviceProxy = type('DeviceProxy', (module.Connection,), attrs) - - -# Patch the extension module -_tango = ExtensionMock(name='_tango') -_tango.constants.TgLibVers = TANGO_VERSION -_tango._get_tango_lib_release.return_value = TANGO_VERSION_INT -set_device_implementations(_tango) -set_device_proxy_implementations(_tango) - - -# Patch modules -sys.modules['tango._tango'] = _tango -sys.modules['tango.constants'] = _tango.constants -print('Mocking tango._tango extension module') - - -# Try to import -import tango -- GitLab From 50df9fc706a8fbc0e24fbeaf64268696e08dfed8 Mon Sep 17 00:00:00 2001 From: Ugur Yilmaz Date: Wed, 10 Jun 2020 09:23:34 +0000 Subject: [PATCH 2/2] delete conf.py customisation codes --- docs/src/conf.py | 261 ----------------------------------------------- 1 file changed, 261 deletions(-) diff --git a/docs/src/conf.py b/docs/src/conf.py index e2f9a5d..81ffaf0 100644 --- a/docs/src/conf.py +++ b/docs/src/conf.py @@ -217,264 +217,3 @@ intersphinx_mapping = {'https://docs.python.org/': None} # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = True - -def copy_spaces(origin): - r = '' - for x in range(len(origin)): - if origin[x] in (' ', '\t'): - r += origin[x] - else: - return r - return r - -def type_to_link(tipus): - if tipus[:9] == 'sequence<' and tipus[-1:] == '>': - return 'sequence<' + type_to_link(tipus[9:-1]) + '>' - #elif tipus in dir(PyTango): - else: - return ':class:`' + tipus + "`" - #else: - # return tipus - -def type_to_pytango_link(tipus): - if tipus[:9] == 'sequence<' and tipus[-1:] == '>': - return 'sequence<' + type_to_link(tipus[9:-1]) + '>' - elif tipus in dir(tango): - return ':class:`' + tipus + "`" - else: - return tipus - -def possible_type_to_link(text): - if len(text) and text[0] == '(' and text[-1] == ')': - return '(' + type_to_link(text[1:-1]) +')' - return text - -def parse_typed_line(line): - spacesSplit = line.strip().split(' ') - first = spacesSplit[0].strip() - return possible_type_to_link(first) + ' ' + ' '.join(spacesSplit[1:]) - -def parse_parameters(line): - spaces = copy_spaces(line) - miniLine = line.strip() - - if miniLine[:2] != '- ': - return line - - spl = miniLine[2:].split(':', 1) - - assert(len(spl) == 2) - - return spaces + ':' + spl[0].strip() + ': ' + parse_typed_line(spl[1]) - - -def parse_bullet_with_type(line): - spaces = copy_spaces(line) - miniLine = line.strip() - - if miniLine[:2] not in ['- ', '* ']: - return line - - spl = miniLine.split(':', 1) - - if len(spl) != 2: - return line - - return spaces + spl[0] + ': ' + parse_typed_line(spl[1]) - - -def parse_throws(line): - words = re.split('(\W+)', line) - assert(line == ''.join(words)) - return ''.join(map(type_to_pytango_link, words)) - - -# http://codedump.tumblr.com/post/94712647/handling-python-docstring-indentation -def docstring_to_lines(docstring): - if not docstring: - return [] - lines = docstring.expandtabs().splitlines() - - # Determine minimum indentation (first line doesn't count): - indent = sys.maxint - for line in lines[1:]: - stripped = line.lstrip() - if stripped: - indent = min(indent, len(line) - len(stripped)) - - # Remove indentation (first line is special): - trimmed = [lines[0].strip()] - if indent < sys.maxint: - for line in lines[1:]: - trimmed.append(line[indent:].rstrip()) - - # Strip off trailing and leading blank lines: - while trimmed and not trimmed[-1]: - trimmed.pop() - while trimmed and not trimmed[0]: - trimmed.pop(0) - return trimmed - -def search_ONLY_signature(name, text): - lines = docstring_to_lines(text) - - # There should be ONE signature and must be the FIRST text - # Signature is the ONLY starting at position 0 - - signatureLine = None - - for ln in range(len(lines)): - line = lines[ln] - - if len(line.strip()) and line[0] != ' ': - parentesis = line.split('(', 1) - fname = parentesis[0].strip() - if len(parentesis)==2 and fname == name.rsplit('.',1)[1]: - if signatureLine is not None: # More than one signature! - return None - signatureLine = ln - else: - return None # There's a text as FIRST text that's NOT the signature! - - if signatureLine is None: - return None - - return lines[signatureLine] - -def split_signature(text): - if text is None: - return None - - # split "fname(params)", "returntype" - ops = text.split('->') - if len(ops) != 2: - return None - - # get rid of "fname" - params = ops[0].strip() - ret_type = ops[1].strip() - p = params.find('(') - if p < 0: - return None - params = params[p:] - return params, ret_type - - - -_with_only_one_signature_methods = {} - -def __reformat_lines(app, what, name, obj, options, lines): - global _with_only_one_signature_methods - if what != 'method': - for ln in range(len(lines)): - lines[ln] = parse_bullet_with_type(lines[ln]) - return - - toinsert = [] - parsingParameters = False - parsingThrows = False - - toinsert.append((0, "")) - - for ln in range(len(lines)): - line = lines[ln] - - if len(line) and line[0] != ' ': - if name in _with_only_one_signature_methods: - # This method has one and only one signature. So it will - # be displayed by sphinx, there's no need for us to fake - # it here... - lines[ln] = "" - else: - parentesis = line.split('(', 1) - fname = parentesis[0].strip() - if len(parentesis)==2 and fname == name.rsplit('.',1)[1]: - sg = split_signature(line) - if sg is not None: - # Main lines are like small titles (**bold**): - lines[ln] = '**' + fname +'** *' + sg[0] + '* **->** ' + type_to_link(sg[1]) - # Add an ENTER after the title, to make a different - # paragraph. So if I have 2 signatures, there's no problem - # with it... - toinsert.append((ln+1, "")) - - ## Main lines are like small titles (**bold**): - #lines[ln]='**' + line.strip() + '**' - ## Add an ENTER after the title, to make a different - ## paragraph. So if I have 2 signatures, there's no problem - ## with it... - #toinsert.append((ln+1, "")) - - - # Mark the "New in this version" lines... - if line.strip()[:14] == "New in PyTango": - lines[ln] = copy_spaces(lines[ln]) + "*" + line.strip() + "*" - parsingParameters = False - parsingThrows = False - - # Look for special control_words - # To replace the actual syntax: "Return : something" - # with the one understood by reStructuredText ":Return: something" - spl = line.strip().split(':', 1) - control_word = spl[0].strip() - - if ((len(spl) != 2) - or (control_word not in ["Parameters", "Return", "Throws", "Example", "See Also" ]) ): - if parsingParameters: - lines[ln] = parse_parameters(line) - elif parsingThrows: - lines[ln] = parse_throws(line) - continue - - parsingParameters = False - parsingThrows = False - spaces = copy_spaces(line) - - # The Example control word is even more special. I will put - # the contents from the following line into a code tag (::) - if control_word == 'Example': - lines[ln] = spaces + ":" + control_word + ": " + spl[1] - toinsert.append((ln+1, "")) - toinsert.append((ln+1, spaces + ' ::')) - toinsert.append((ln+1, "")) - elif control_word == 'Parameters': - lines[ln] = spaces + ":Parameters:" + parse_parameters(spl[1]) - parsingParameters = True - elif control_word == 'Return': - lines[ln] = spaces + ":Return: " + parse_typed_line(spl[1]) - elif control_word == "Throws": - lines[ln] = spaces + ":Throws:" + parse_throws(spl[1]) - parsingThrows = True - else: - lines[ln] = spaces + ":" + control_word + ": " + spl[1] - - for x in range(len(toinsert)-1, -1, -1): - pos, txt = toinsert[x] - lines.insert(pos, txt) - - -def __process_signature(app, what, name, obj, options, signature, return_annotation): - global _with_only_one_signature_methods - if what != 'method': - return - sg = split_signature(search_ONLY_signature(name, obj.__doc__)) - if sg is not None: - _with_only_one_signature_methods[name] = True - return sg - return (signature, return_annotation) - -def setup(app): - # sphinx will call these methods when he finds an object to document. - # I want to edit the docstring to adapt its format to something more - # beautiful. - # I also want to edit the signature because boost methods have no - # signature. I will read the signature from the docstring. - # The order sphinx will call it is __process_signature, __reformat_lines. - # And it is important because I keep some information between the two - # processes - # Problem is __process_signature works great with python methods... - # but is not even called for methods defined by boost. So, as it is, - # is useless now. - - #app.connect('autodoc-process-signature', __process_signature) - app.connect('autodoc-process-docstring', __reformat_lines) -- GitLab