Commit d936b141 authored by Jérome Perrin's avatar Jérome Perrin

jedi save point

parent 08c10242
......@@ -9,6 +9,7 @@ import inspect
logger = logging.getLogger("erp5.extension.Jedi")
#logger.info('included plugin loaded')
import os
import jedi
import zope.dottedname.resolve
import time
......@@ -48,8 +49,6 @@ except ImportError:
def makeERP5Plugin():
logger.info('making erp5 plugin')
import os
from jedi.evaluate.context import ModuleContext
from jedi.evaluate.lazy_context import LazyTreeContext
from jedi.evaluate.helpers import get_str_or_none
......@@ -119,7 +118,7 @@ def makeERP5Plugin():
return wrapper
def import_module(self, callback):
def not_used_import_module(self, callback):
"""
Handle ERP5 dynamic modules import.
"""
......@@ -484,10 +483,24 @@ from Products.PythonScripts.PythonScript import PythonScript # pylint: disable
from collections import namedtuple, defaultdict
def SkinsTool_getStub(self):
def SkinsTool_getClassSet(self):
portal = self.getPortalObject()
sources = []
print('SkinsTool_getStub start')
class_set = set([])
# TODO: sort by default skin selection and use the ones registered in skin selections
for skin_folder in portal.portal_skins.objectValues():
for script in skin_folder.objectValues(spec=('Script (Python)',
'External Method')):
if not '_' in script.getId():
logger.debug('Skipping wrongly named script %s', script.getId())
continue
type_ = script.getId().split('_')[0]
class_set.add(type_)
return class_set
def SkinsTool_getStubForClass(self, class_name):
portal = self.getPortalObject()
line_list = []
SkinDefinition = namedtuple(
'SkinDefinition', 'id,docstring,type_comment,skin_folder,params')
import parso
......@@ -504,8 +517,11 @@ def SkinsTool_getStub(self):
logger.debug('Skipping wrongly named script %s', script.getId())
continue
type_ = script.getId().split('_')[0]
if type_ != class_name:
continue
docstring = '"""External method"""'
params = ''
type_comment = ''
if script.meta_type == 'Script (Python)':
body = script.body()
params = script.params()
......@@ -514,14 +530,14 @@ def SkinsTool_getStub(self):
icon_path = script.om_icons()[0]['path']
icon_alt = script.om_icons()[0]['alt']
docstring_first_line = (
"![{icon_alt}]({portal_url}/{icon_path}) "
"[`{script_id}`]({portal_url}/portal_skins/{skin_folder_id}/{script_id}/manage_main)\n").format(
"![{icon_alt}]({portal_url}/{icon_path}) "
"[`{script_id}`]({portal_url}/portal_skins/{skin_folder_id}/{script_id}/manage_main)\n"
).format(
icon_alt=icon_alt,
portal_url=portal.absolute_url(),
icon_path=icon_path,
script_id=script.getId(),
skin_folder_id=skin_folder.getId()
)
skin_folder_id=skin_folder.getId())
docstring = '"""{}"""'.format(docstring_first_line)
module = grammar.parse(body)
if next(iter(grammar.iter_errors(module)), None) is not None:
......@@ -536,14 +552,18 @@ def SkinsTool_getStub(self):
if first_leaf.type == 'string':
original_docstring = first_leaf.value
if original_docstring.startswith("'''"):
docstring = "'''{}\n{}".format(docstring_first_line, original_docstring[3:])
docstring = "'''{}\n{}".format(
docstring_first_line, original_docstring[3:])
elif original_docstring.startswith("'"):
docstring = "'''{}\n{}''".format(docstring_first_line, original_docstring[1:])
docstring = "'''{}\n{}''".format(
docstring_first_line, original_docstring[1:])
elif original_docstring.startswith('"""'):
docstring = '"""{}\n{}'.format(docstring_first_line, original_docstring[3:])
docstring = '"""{}\n{}'.format(
docstring_first_line, original_docstring[3:])
elif original_docstring.startswith('"'):
docstring = '"""{}\n{}""'.format(docstring_first_line, original_docstring[1:])
docstring = '"""{}\n{}""'.format(
docstring_first_line, original_docstring[1:])
skin_by_type[type_].append(
SkinDefinition(
script.getId(),
......@@ -553,12 +573,11 @@ def SkinsTool_getStub(self):
params))
for type_, skins in skin_by_type.items():
sources.append(
line_list.append(
textwrap.dedent(
"""\
import erp5.portal_type
from erp5 import portal_type
from erp5.portal_type import Organisation as erp5_portal_type_Person
class {class_name}:
{docstring}
......@@ -568,8 +587,9 @@ def SkinsTool_getStub(self):
docstring=safe_docstring("Skins for {}".format(type_))))
for skin in skins:
skin = skin # type: SkinDefinition
sources.append(
# ( the comment is also here so that dedent keep indentation )
line_list.append(
# the comment is also here so that dedent keep indentation, because this method block needs
# more indentation than class block
textwrap.dedent(
"""\
# {skin_id} in {skin_folder}
......@@ -581,11 +601,10 @@ def SkinsTool_getStub(self):
params=skin.params,
type_comment=skin.type_comment,
docstring=skin.docstring))
print('SkinsTool_getStub finished')
return "\n".join(sources)
return "\n".join(line_list)
def TypesTool_getStub(self):
def TypesTool_getStub(self): # XXX useless
print('TypesTool_getStub start')
portal = self.getPortalObject()
sources = [TypesTool_getStubHeader(self)]
......@@ -635,6 +654,7 @@ def TypesTool_getStubHeader(self):
''')
# TODO: drop include_header
def TypeInformation_getStub(self, include_header=True):
# type: (ERP5TypeInformation) -> str
"""returns a .pyi stub file for this portal type
......@@ -643,6 +663,9 @@ def TypeInformation_getStub(self, include_header=True):
"""
portal = self.getPortalObject()
# TODO: getParentValue
# TODO: a class for magic things like getPortalObject ?
@WorkflowMethod.disable
def makeTempClass():
# everything is allowed in portal trash so we create our
......@@ -664,10 +687,10 @@ def TypeInformation_getStub(self, include_header=True):
parent_class_module = parent_class.__module__
include_private = False
imports = []
imports = ['import erp5.portal_type'] # TODO: this can be a set (all imports)
header = ""
if include_header:
header = portal.portal_types.TypesTool_getStubHeader()
header = TypesTool_getStubHeader(self.getPortalObject().portal_types)
methods = []
debug = ""
method_template_template = """ {decorator}\n def {method_name}({method_args}) -> {return_type}:\n {docstring}"""
......@@ -680,6 +703,16 @@ def TypeInformation_getStub(self, include_header=True):
return_type='str',
docstring=safe_docstring(self.getId())))
imports.append('from erp5.portal_type import ERP5Site')
methods.append(
method_template_template.format(
decorator='',
method_name='getPortalObject',
method_args="self",
return_type='ERP5Site',
docstring=safe_docstring(
getattr(temp_class.getPortalObject, '__doc__', None) or '...')))
# first class contain workflow and some constraints.
for property_name in sorted(vars(temp_class)):
if property_name[0] == '_' and not include_private:
......@@ -853,6 +886,22 @@ def TypeInformation_getStub(self, include_header=True):
safe_python_identifier(pc.__name__), prefixed_class_name))
base_classes.append(prefixed_class_name)
# everything can use ERP5Site_ skins
imports.append('from erp5.skins_tool import ERP5Site as skins_tool_ERP5Site')
base_classes.append('skins_tool_ERP5Site')
base_classes.append(prefixed_class_name)
# XXX special methods for some tools
if self.getId() == 'Simulation Tool':
methods.append(
method_template_template.format(
decorator='',
method_name='getInventoryList',
method_args="self", # TODO
return_type='erp5.portal_type.Person',
docstring=safe_docstring('TODO =)')))
class_template = textwrap.dedent(
"""\
{header}
......@@ -1108,20 +1157,22 @@ from Products.ERP5.ERP5Site import ERP5Site # pylint: disable=unused-import
def ERP5Site_getPortalStub(self):
# type: (ERP5Site) -> str
# TODO:
module_stub_template = textwrap.dedent(
'''\
def __{module_id}(self):
from erp5.portal_type import {module_class_name}
return {module_class_name}()
{module_id} = property(__{module_id})
''')
'''
@property
def {module_id}(self):
from erp5.portal_type import {module_class_name}
return {module_class_name}()
''')
tool_stub_template = textwrap.dedent(
'''\
def __{tool_id}(self):
{tool_import}
return {tool_class}()
{tool_id} = property(__{tool_id})
''')
'''
@property
def {tool_id}(self):
{tool_import}
return {tool_class}()
''')
source = []
for m in self.objectValues():
if m.getPortalType().endswith('Module'):
......@@ -1137,29 +1188,31 @@ def ERP5Site_getPortalStub(self):
m.__class__.__module__,
tool_class,)
if m.getId() == 'portal_catalog':
tool_class = 'ICatalogTool'
tool_import = ''
tool_class = 'ICatalogTool' # XXX these I prefix are stupid
tool_import = 'from erp5.portal_type import ICatalogTool'
source.extend(
tool_stub_template.format(
tool_id=m.getId(), tool_class=tool_class,
tool_import=tool_import).splitlines())
tool_import=tool_import).splitlines(),
)
# TODO: tools with at least base categories for CategoryTool
return textwrap.dedent(
'''\
from Products.ERP5Site.ERP5Site import ERP5Site
class IPortalObject(ERP5Site):
{}
''').format('\n '.join(source))
'''
from Products.ERP5Site.ERP5Site import ERP5Site as ERP5Site_parent_ERP5Site
from erp5.skins_tool import ERP5Site as skins_tool_ERP5Site
class ERP5Site(ERP5Site_parent_ERP5Site, skins_tool_ERP5Site):
{}
''').format('\n '.join(source))
def CatalogTool_getStub(self):
portal = self.getPortalObject() # type: IPortalObject
portal = self.getPortalObject()
brain_class_list = []
imports = []
brain_template = textwrap.dedent(
'''\
from typing import TypeVar, Generic
from typing import TypeVar, Generic, List, Union
T = TypeVar('T')
class Brain(Generic[T]):
id: str
......@@ -1178,7 +1231,7 @@ def CatalogTool_getStub(self):
return textwrap.dedent(
'''\
from Products.ERP5Catalog.Tool import ERP5CatalogTool
from Products.ERP5Catalog.Tool.ERP5CatalogTool import ERP5CatalogTool
{brain_template}
CatalogToolAllTypes = Union[{all_types}]
class ICatalogTool(ERP5CatalogTool):
......@@ -1189,3 +1242,156 @@ def CatalogTool_getStub(self):
''').format(
brain_template=brain_template, all_types=all_types)
def ERP5Site_dumpModuleCode(self, component_or_script=None):
"""Save code in filesystem for jedi to use it.
Generate stubs for erp5.* dynamic modules and copy the in-ZODB modules
to files.
"""
def mkdir_p(path):
if not os.path.exists(path):
os.mkdir(path, 0o700)
portal = self.getPortalObject() # type: 'erp5.portal_type.ERP5Site'
module_dir = '/tmp/ahaha/erp5/' # TODO
mkdir_p(module_dir)
# generate erp5/__init__.py
with open(
os.path.join(module_dir, '__init__.py'),
'w',) as erp5__init__f:
for module in (
'portal_type',
'accessor_holder',
'skins_tool',
'component',):
erp5__init__f.write('from . import {module}\n'.format(module=module))
# TODO: accessor_holder real name ?
# TODO: component versions ?
mkdir_p(os.path.join(module_dir, module))
if module == 'portal_type':
with open(
os.path.join(
module_dir,
module,
'__init__.py',),
'w',) as module_f:
for ti in portal.portal_types.contentValues():
class_name = safe_python_identifier(ti.getId())
module_f.write(
'from .{class_name} import {class_name}\n'.format(
class_name=class_name))
with open(
os.path.join(
module_dir,
module,
'{class_name}.pyi'.format(class_name=class_name),
),
'w',
) as type_information_f:
type_information_f.write(
ti.TypeInformation_getStub().encode('utf-8'))
# tools XXX don't do here but in TypeInformation_getStub ?
#module_f.write('from .CatalogTool import CatalogTool')
module_f.write('from .ICatalogTool import ICatalogTool\n')
with open(
os.path.join(
module_dir,
module,
'ICatalogTool.pyi',),
'w',) as portal_f:
portal_f.write(
CatalogTool_getStub(self.getPortalObject().portal_catalog))
module_f.write('from .SimulationTool import SimulationTool\n')
# TOODO: simulation tool
# portal object
module_f.write('from .ERP5Site import ERP5Site\n')
module_f.write(
'from .ERP5Site import ERP5Site as IPortalObject\n'
) # XXX what name for portal ? -> ERP5Site !
with open(
os.path.join(
module_dir,
module,
'ERP5Site.pyi',),
'w',) as portal_f:
portal_f.write(ERP5Site_getPortalStub(self.getPortalObject()))
elif module == 'accessor_holder':
# TODO: real path is accessor_holder.something !?
with open(
os.path.join(module_dir, module, '__init__.py'),
'w',) as accessor_holder_f:
for ps in portal.portal_property_sheets.contentValues():
class_name = safe_python_identifier(ps.getId())
accessor_holder_f.write(
'from .{class_name} import {class_name}\n'.format(
class_name=class_name))
with open(
os.path.join(
module_dir,
module,
'{class_name}.pyi'.format(class_name=class_name),
),
'w',
) as property_sheet_f:
property_sheet_f.write(ps.PropertySheet_getStub().encode('utf-8'))
elif module == 'skins_tool':
skins_tool = portal.portal_skins
with open(
os.path.join(module_dir, module, '__init__.py'),
'w',) as skins_tool_f:
for class_name in SkinsTool_getClassSet(skins_tool):
skins_tool_f.write(
'from {class_name} import {class_name}\n'.format(
class_name=class_name))
with open(
os.path.join(
module_dir,
module,
'{}.pyi'.format(class_name),),
'w',
) as skin_f:
skin_f.write(
SkinsTool_getStubForClass(
skins_tool,
class_name,).encode('utf-8'))
elif module == 'component':
module_to_component_portal_type_mapping = {
'test': 'Test Component',
'document': 'Document Component',
'extension': 'Extension Component',
'tool': 'Tool Component',
'module': 'Module Component',
'interface': 'Interface Component',
}
with open(
os.path.join(module_dir, module, '__init__.py'),
'w',) as component_module__init__f:
for sub_module, portal_type in module_to_component_portal_type_mapping.items():
component_module__init__f.write(
'from . import {}\n'.format(sub_module))
mkdir_p(os.path.join(module_dir, module, sub_module))
with open(
os.path.join(module_dir, module, sub_module, '__init__.py'),
'w',
) as component_sub_module_init_f:
for brain in portal.portal_catalog(
portal_type=portal_type, validation_state=('validated',)):
component = brain.getObject()
component_sub_module_init_f.write(
"from {component_reference} import {component_reference}\n"
.format(component_reference=component.getReference()))
with open(
os.path.join(
module_dir,
module,
sub_module,
'{}.py'.format(component.getReference()),
),
'w',
) as component_f:
component_f.write(component.getTextContent()) #.encode('utf-8'))
return 'done'
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ExternalMethod" module="Products.ExternalMethod.ExternalMethod"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_function</string> </key>
<value> <string>ERP5Site_dumpModuleCode</string> </value>
</item>
<item>
<key> <string>_module</string> </key>
<value> <string>Jedi</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>ERP5Site_dumpModuleCode</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment