Closed eirannejad closed 7 years ago
Thanks for sharing this @eirannejad . I have been using this: https://github.com/gtalarico/revitpythonwrapper/blob/master/rpw/utils/sphinx_compat.py
Would mind explaining it a bit more? How does it handle the .dot imports? Are you running sphinx on cpyhon or ironpython? The code I shared above works by making sure the code can be loaded (if it were to be executed fully, it would fail), but the way I load the modules, it works well.
@eirannejad Excellent!! - I was struggeling with this today, while trying to get our revit helpers into sphinx. Somehow I could not get the mock for "import clr" working properly (sphinx's conf.py accepst it, but not the loaded revit helper scripts..). In your setup is sphinx ran from CPython? (or Ironpython?) Yes a bit of explanation would be very much appreciated and helpful - thank you!!
Yes so I'm running sphinx on Cpython 3 on mac OS so I had to fake all dot net imports for the program to work. (This is also the reason behind all the #noinspection
directives in the code to exclude all these dot net imports from pyCharm's inspection engine.)
python runs the find_module()
method of the class above when it can't find any of the modules being imported and this method basically fakes the import and adds it to sys.modules
I also defined a __sphinx__
parameter in the conf.py
file and check its availability in the actual code to protect it when it's being run under sphinx. All module level statements will not run if the script is in documentation mode (that is when __sphinx__
is defined.)
if not __sphinx__:
# do the module level stuff
@eirannejad Thanks a lot for the explanation!! It made me re-watch David Beazley's excellent talk on packages to hopefully understand better: https://www.youtube.com/watch?v=0oTh1CXRaQ0 I pulled your meta_path class into my simple clr dummy module (attached below) which lives in my cpython site-packages. so "import clr", "clr.AddReference("RevitAPI")" and "import Autodesk" and the like get tricked properly, but as soon as an Autodesk class attributes get accessed like "XYZ.Zero" it unfortunately falls apart. Are these caught by your "if not sphinx"?
@gtalarico out of curiosity: Since you run the awesome revitapidocs - do you get a non-dll version or stubs out of processing RevitAPI that could feed cpython with Sphinx (or an IDE for completions)?
import sys
import imp
print("clr fake loaded.")
def AddReference(ref_name):
print("reference add {0} faked.".format(ref_name))
class DotNetImporter(object):
domain_modules = ['System', 'Autodesk', 'Microsoft']
found_mods = dict()
def find_module(self, fullname, path=None):
if fullname in self.domain_modules:
return self
if path:
for p in path:
if p in self.domain_modules:
self.domain_modules.append(fullname)
return self
return None
def load_module(self, fullname):
if fullname in sys.modules:
return sys.modules[fullname]
mod = imp.new_module(fullname)
mod.__loader__ = self
sys.modules[fullname] = mod
mod.__file__ = fullname
mod.__path__ = [fullname]
return mod
# add importer to the list
sys.meta_path.append(DotNetImporter())
@hdm-dt-fb So I use this import-wrapper class to manage all the major imports and it catches all import statements in my code. For everything else, especially module level code, I'm protecting the code from execution and giving None
or empty values to global variables. pyRevit module has a EXEC_PARAMS.doc_mode
property that has the value of __sphinx__
and it's used everywhere is main pyRevit to protect module level code.
if EXEC_PARAMS.doc_mode:
# if in doc mode, give empty values to globals
PYREVIT_APP_DIR = PYREVIT_VERSION_APP_DIR = ' '
else:
# otherwise, define the values
PYREVIT_APP_DIR = op.join(USER_ROAMING_DIR, PYREVIT_ADDON_NAME)
PYREVIT_VERSION_APP_DIR = op.join(PYREVIT_APP_DIR, HOST_APP.version)
@eirannejad now I got it, that is interesting!! sorry it took me bit - thank you!! I definitively have to (re-)read more pyRevit! @gtalarico thank you so much!!
@eirannejad ..one thing I was wondering: I saw "import builtin" in your docs/conf.py - I thought it was renamed to "import builtins" in python3? Or do you use a special module/package there? (at least my python3.6 complained on builtin)
Correct that module is renamed to builtins
in python 3
@gtalarico I ended up defining the class below in the sphinx
conf.py
that will handle all dot net imports:I'm running sphinx on a mac to create the docs for pyRevit and is working well. I also had to define global
__sphinx__
parameter to protect the module level code from execution.