Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
erp5
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Labels
Merge Requests
7
Merge Requests
7
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Jobs
Commits
Open sidebar
Jérome Perrin
erp5
Commits
0766d41f
Commit
0766d41f
authored
Nov 04, 2019
by
Jérome Perrin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
jedi WIP
parent
1f0eba38
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
369 additions
and
206 deletions
+369
-206
bt5/erp5_monaco_editor/ExtensionTemplateItem/portal_components/extension.erp5.Jedi.py
...sionTemplateItem/portal_components/extension.erp5.Jedi.py
+362
-205
bt5/erp5_monaco_editor/ExtensionTemplateItem/portal_components/extension.erp5.Jedi.xml
...ionTemplateItem/portal_components/extension.erp5.Jedi.xml
+7
-1
No files found.
bt5/erp5_monaco_editor/ExtensionTemplateItem/portal_components/extension.erp5.Jedi.py
View file @
0766d41f
# pylint: disable=unused-variable
from
__future__
import
unicode_literals
import
json
import
sys
...
...
@@ -7,7 +6,6 @@ import logging
import
inspect
logger
=
logging
.
getLogger
(
"erp5.extension.Jedi"
)
#logger.info('included plugin loaded')
import
os
import
jedi
...
...
@@ -17,7 +15,8 @@ import time
last_reload_time
=
time
.
time
()
# increase default cache duration
jedi
.
settings
.
call_signatures_validity
=
30
# XXX needed ?
# XXX I'm really not sure this is needed, jedi seems fast enough.
jedi
.
settings
.
call_signatures_validity
=
30
# monkey patch to disable buggy sys.path addition based on buildout.
# rdiff-backup seem to trigger a bug, but it's generally super slow and not correct for us.
...
...
@@ -41,18 +40,96 @@ jedi_api_project.discover_buildout_paths = dont_discover_buildout_paths
try
:
# for local types annotations in this component
from
erp5.portal_type
import
IPortalObject
# pylint: disable=unused-import
from
erp5.portal_type
import
ERP5Site
# pylint: disable=unused-import
except
ImportError
:
pass
def
executeJediXXX
(
callback
,
context
,
arguments
):
from
jedi.evaluate.base_context
import
ContextSet
# XXX function for relaodability
def
call
():
return
callback
(
context
,
arguments
=
arguments
)
from
jedi.evaluate.context.instance
import
TreeInstance
from
jedi.evaluate.gradual.typing
import
InstanceWrapper
from
jedi.evaluate.lazy_context
import
LazyKnownContexts
from
jedi.evaluate.base_context
import
ContextSet
,
NO_CONTEXTS
def
makeFilterFunc
(
class_from_portal_type
,
arguments
):
def
filter_func
(
val
):
if
isinstance
(
val
,
TreeInstance
)
and
val
.
tree_node
.
type
==
'classdef'
:
logger
.
info
(
"classdef cool => %s == %s"
,
val
.
tree_node
.
name
.
value
,
class_from_portal_type
)
return
val
.
tree_node
.
name
.
value
==
class_from_portal_type
if
isinstance
(
val
,
LazyKnownContexts
)
and
filter_func
(
val
.
infer
()):
return
True
if
isinstance
(
val
,
ContextSet
):
return
val
.
filter
(
filter_func
)
!=
NO_CONTEXTS
if
isinstance
(
val
,
InstanceWrapper
):
for
wrapped
in
val
.
iterate
():
if
filter_func
(
wrapped
):
return
True
annotation_classes
=
val
.
gather_annotation_classes
()
import
pdb
;
pdb
.
set_trace
()
return
val
.
gather_annotation_classes
().
filter
(
filter_func
)
# XXX
if
str
(
val
).
count
(
'<LazyGenericClass:'
)
==
1
:
logger
.
info
(
"I don't know ... %s because %s in %s"
,
"{}@"
.
format
(
class_from_portal_type
)
in
str
(
val
),
"{}@"
.
format
(
class_from_portal_type
),
val
)
return
"{}@"
.
format
(
class_from_portal_type
)
in
str
(
val
)
logger
.
info
(
"not found in %s"
,
val
)
return
False
return
filter_func
# methods returning portal types
if
context
.
is_function
():
# and 1 or context.get_function_execution(
#).function_context.name.string_name == 'newContent':
if
not
arguments
.
argument_node
:
return
call
()
# no portal_type, we'll use what's defined in the stub
# look for a "portal_type=" argument
for
arg_name
,
arg_value
in
arguments
.
unpack
():
if
arg_name
==
'portal_type'
:
try
:
portal_type
=
iter
(
arg_value
.
infer
()).
next
().
get_safe_value
()
except
Exception
:
logger
.
exception
(
"error infering"
)
continue
if
not
isinstance
(
portal_type
,
str
):
continue
logger
.
info
(
'ahah portal_type based method with portal type=%s ...'
,
portal_type
)
# XXX this is really horrible
original
=
call
()
filtered
=
original
.
filter
(
makeFilterFunc
(
portal_type
.
replace
(
' '
,
''
),
arguments
))
#original._set = frozenset(
# {x for x in original._set if class_from_portal_type in str(x)})
logger
.
info
(
'portal_type based method, returning
\
n
%s instead of
\
n
%s'
,
filtered
,
original
)
return
filtered
# methods returning List of portal types
# methods returning List of Brain of portal types
return
call
()
def
makeERP5Plugin
():
logger
.
info
(
'making erp5 plugin'
)
from
jedi.evaluate.context
import
ModuleContext
from
jedi.evaluate.lazy_context
import
LazyTreeContext
from
jedi.evaluate.helpers
import
get_str_or_none
from
jedi.evaluate.gradual.stub_context
import
TypingModuleWrapper
,
StubModuleContext
from
jedi.evaluate.base_context
import
ContextSet
from
jedi.parser_utils
import
get_cached_code_lines
from
StringIO
import
StringIO
...
...
@@ -60,8 +137,8 @@ def makeERP5Plugin():
class
JediERP5Plugin
(
object
):
_cache
=
{}
def
_getPortalObject
(
self
):
# type: () ->
IPortalObject
def
_getPortalObject
(
self
):
# XXX needed ?
# type: () ->
ERP5Site
from
Products.ERP5.ERP5Site
import
getSite
from
Products.ERP5Type.Globals
import
get_request
from
ZPublisher.BaseRequest
import
RequestContainer
...
...
@@ -75,6 +152,9 @@ def makeERP5Plugin():
logger
.
info
(
"JediERP5Plugin registering execute"
)
def
wrapper
(
context
,
arguments
):
from
erp5.component.extension.Jedi
import
executeJediXXX
as
_execute
return
_execute
(
callback
,
context
,
arguments
)
def
call
():
return
callback
(
context
,
arguments
=
arguments
)
...
...
@@ -91,7 +171,7 @@ def makeERP5Plugin():
if
arg_name
==
'portal_type'
:
try
:
portal_type
=
iter
(
arg_value
.
infer
()).
next
().
get_safe_value
()
except
Exception
as
e
:
except
Exception
:
logger
.
exception
(
"error infering"
)
continue
if
not
isinstance
(
portal_type
,
str
):
...
...
@@ -102,6 +182,10 @@ def makeERP5Plugin():
# XXX this is really horrible
class_from_portal_type
=
portal_type
.
replace
(
' '
,
''
)
+
'@'
original
=
call
()
if
0
:
filtered
=
ContextSet
.
from_sets
({
s
for
s
in
original
})
import
pdb
pdb
.
set_trace
()
original
.
_set
=
frozenset
({
x
for
x
in
original
.
_set
if
class_from_portal_type
in
str
(
x
)
})
...
...
@@ -118,10 +202,11 @@ def makeERP5Plugin():
return
wrapper
def
not_used_import_module
(
self
,
callback
):
def
not_used_import_module
(
self
,
callback
):
# TODO: remove, not used
"""
Handle ERP5 dynamic modules import.
"""
from
jedi.evaluate.context
import
ModuleContext
,
StubModuleContext
logger
.
info
(
"JediERP5Plugin registering import_module"
)
def
wrapper
(
evaluator
,
import_names
,
module_context
,
*
args
,
**
kwargs
):
...
...
@@ -350,7 +435,7 @@ def _guessType(name, context_type=None):
if
name
in
(
'context'
,
'container'
,):
return
'
Products.ERP5Type.Core.Folder.Folder
'
return
'
ERP5Site
'
if
name
==
'script'
:
return
'Products.PythonScripts.PythonScript'
if
name
==
'REQUEST'
:
...
...
@@ -403,20 +488,17 @@ def ERP5Site_getPythonSourceCodeCompletionList(self, data, REQUEST=None):
context_type
=
None
if
'_'
in
script_name
:
context_type
=
script_name
.
split
(
'_'
)[
0
]
if
context_type
==
'ERP5Site'
:
context_type
=
'IPortalObject'
else
:
if
context_type
not
in
[
ti
.
replace
(
' '
,
''
)
for
ti
in
portal
.
portal_types
.
objectIds
()
]:
logger
.
warning
(
"context_type %s has no portal type, using Folder"
,
context_type
)
context_type
=
None
if
context_type
not
in
[
ti
.
replace
(
' '
,
''
)
for
ti
in
portal
.
portal_types
.
objectIds
()]
+
[
'ERP5Site'
,]:
logger
.
warning
(
"context_type %s has no portal type, using ERP5Site"
,
context_type
)
context_type
=
None
imports
=
"from erp5.portal_type import {}; import Products.ERP5Type.Core.Folder; import ZPublisher.HTTPRequest; import Products.PythonScripts"
.
format
(
context_type
or
'Base'
)
# import Base to have a valid import in that case ...
context_type
or
'ERP5Site'
)
type_annotation
=
" # type: ({}) -> None"
.
format
(
', '
.
join
(
[
_guessType
(
part
,
context_type
)
for
part
in
signature_parts
]))
...
...
@@ -604,58 +686,7 @@ def SkinsTool_getStubForClass(self, class_name):
return
"
\
n
"
.
join
(
line_list
)
def
TypesTool_getStub
(
self
):
# XXX useless
print
(
'TypesTool_getStub start'
)
portal
=
self
.
getPortalObject
()
sources
=
[
TypesTool_getStubHeader
(
self
)]
for
ti
in
portal
.
portal_types
.
contentValues
():
# XXX skip a few types that we never use directly to keep the file small.
# XXX this seems a bad idea now.
if
ti
.
getId
().
split
()[
-
1
]
in
(
'Constraint'
,
'Property'
):
logger
.
info
(
'Skipping stub generation for %s'
,
ti
.
getId
())
continue
try
:
sources
.
append
(
TypeInformation_getStub
(
ti
,
include_header
=
False
))
except
Exception
:
logger
.
exception
(
'Error generating stub for %s'
,
ti
.
getId
())
sources
.
append
(
CatalogTool_getStub
(
portal
))
sources
.
append
(
ERP5Site_getPortalStub
(
portal
))
sources
.
append
(
"Type_AnyPortalType = {Type_AnyPortalType}"
.
format
(
Type_AnyPortalType
=
'Union[{}]'
.
format
(
', '
.
join
(
safe_python_identifier
(
ti
)
for
ti
in
portal
.
portal_types
.
contentIds
()
if
not
ti
.
endswith
(
' Module'
)
# XXX we don't need modules here
))))
print
(
'TypesTool_getStub finished'
)
# XXX everytime jedi sees a "import erp5.accessor_holder" line, this will load our plugin.
return
"import erp5.accessor_holder
\
n
"
+
"
\
n
"
.
join
(
[
line
for
line
in
sources
if
line
!=
'import erp5.accessor_holder'
])
def
TypesTool_getStubHeader
(
self
):
return
textwrap
.
dedent
(
'''
\
from typing import Union, List, Optional, Any, overload, Literal, TypeVar, Generic
from DateTime import DateTime
T = TypeVar('T')
class Brain(Generic[T]):
id: str
path: str
# XXX more depending on select_list or select_dict and getCatalogSearchResultKeys
def getObject(self) -> T:
...
'''
)
# TODO: drop include_header
def
TypeInformation_getStub
(
self
,
include_header
=
True
):
def
TypeInformation_getStub
(
self
):
# type: (ERP5TypeInformation) -> str
"""returns a .pyi stub file for this portal type
...
...
@@ -686,11 +717,14 @@ def TypeInformation_getStub(self, include_header=True):
parent_class
=
temp_class
.
mro
()[
1
]
parent_class_module
=
parent_class
.
__module__
include_private
=
False
imports
=
[
'import erp5.portal_type'
]
# TODO: this can be a set (all imports)
imports
=
set
([
'from erp5.portal_type import Type_CatalogBrain'
,
'from erp5.portal_type import Type_AnyPortalTypeList'
,
'from erp5.portal_type import Type_AnyPortalTypeCatalogBrainList'
,
'from typing import Union, List, Optional, Any, overload, Literal, TypeVar, Generic'
,
'from DateTime import DateTime.DateTime as DateTime # XXX help jedi'
,
])
header
=
""
if
include_header
:
header
=
TypesTool_getStubHeader
(
self
.
getPortalObject
().
portal_types
)
methods
=
[]
debug
=
""
method_template_template
=
""" {decorator}
\
n
def {method_name}({method_args}) -> {return_type}:
\
n
{docstring}"""
...
...
@@ -700,10 +734,24 @@ def TypeInformation_getStub(self, include_header=True):
decorator
=
''
,
method_name
=
'getPortalType'
,
method_args
=
"self"
,
return_type
=
'str'
,
docstring
=
safe_docstring
(
self
.
getId
())))
return_type
=
'Literal["{}"]'
.
format
(
self
.
getId
()),
# We want to be able to infer based on the portal type named returned by x.getPortalType()
# jedi does not support Literal in this context, so add a method implementation.
# This is not really valid for a .pyi, but jedi does not care.
docstring
=
"{}
\
n
return '{}'"
.
format
(
safe_docstring
(
self
.
getId
()),
self
.
getId
())))
# XXX debug
methods
.
append
(
method_template_template
.
format
(
decorator
=
''
,
method_name
=
'reveal_portal_tye_{}'
.
format
(
safe_python_identifier
(
self
.
getId
())),
method_args
=
'self'
,
return_type
=
''
,
docstring
=
safe_docstring
(
"ahaha cool :)"
)))
imports
.
a
ppen
d
(
'from erp5.portal_type import ERP5Site'
)
imports
.
a
d
d
(
'from erp5.portal_type import ERP5Site'
)
methods
.
append
(
method_template_template
.
format
(
decorator
=
''
,
...
...
@@ -715,10 +763,11 @@ def TypeInformation_getStub(self, include_header=True):
# first class contain workflow and some constraints.
for
property_name
in
sorted
(
vars
(
temp_class
)):
if
property_name
[
0
]
==
'_'
and
not
include_private
:
if
property_name
[
0
]
==
'_'
:
continue
property_value
=
getattr
(
temp_class
,
property_name
)
if
isinstance
(
property_value
,
Constant
.
Getter
):
# TODO: add an implementation returning the value so that jedi can infer
methods
.
append
(
method_template_template
.
format
(
decorator
=
''
,
...
...
@@ -731,6 +780,7 @@ def TypeInformation_getStub(self, include_header=True):
WorkflowState
.
TranslatedGetter
,
WorkflowState
.
TranslatedTitleGetter
,
WorkflowState
.
Getter
)):
# TODO: docstring (with link to workflow)
methods
.
append
(
method_template_template
.
format
(
decorator
=
''
,
...
...
@@ -739,6 +789,9 @@ def TypeInformation_getStub(self, include_header=True):
return_type
=
"str"
,
docstring
=
safe_docstring
(
'TODO %s'
%
property_value
)))
elif
isinstance
(
property_value
,
WorkflowMethod
):
# TODO: docstring (with link to workflow)
# TODO: also docstring for interaction methods (and maybe something clever so that if we
# have an interaction on _setSomething the docstring of setSomething mention it).
methods
.
append
(
method_template_template
.
format
(
decorator
=
''
,
...
...
@@ -770,65 +823,35 @@ def TypeInformation_getStub(self, include_header=True):
safe_python_identifier
(
t
)
for
t
in
allowed_content_types
]
if
allowed_content_types
and
hasattr
(
temp_class
,
'contentValues'
):
if
include_header
:
for
allowed
in
allowed_content_types_classes
:
imports
.
append
(
'from erp5.portal_type import {}'
.
format
(
allowed
))
for
allowed
in
allowed_content_types_classes
:
imports
.
add
(
'from erp5.portal_type import {}'
.
format
(
allowed
))
if
len
(
allowed_content_types
)
==
1
:
subdocument_type
=
'{}'
.
format
(
allowed_content_types_classes
[
0
])
overloads
=
[]
else
:
subdocument_type
=
'Union[{}]'
.
format
(
', '
.
join
(
allowed_content_types_classes
))
# TODO: getParentValue
# XXX: what's wrong with getSourceValue(portal_type='Organisation')
overloads
=
[
]
# XXX this was bad idea, jedi does not supports overloads and Literal types
for
method_name
in
(
'contentValues'
,
'objectValues'
,
'searchFolder'
):
return_type
=
'List[{}]'
.
format
(
subdocument_type
)
if
method_name
==
'searchFolder'
:
return_type
=
'List[Brain[{}]]'
.
format
(
subdocument_type
)
return_type
=
'List[
Type_Catalog
Brain[{}]]'
.
format
(
subdocument_type
)
if
len
(
allowed_content_types
)
>
1
:
# not correct but it makes jedi complete well when portal_type='one'
return_type
=
'Union[{}]'
.
format
(
', '
.
join
((
'List[Brain[{}]]'
.
format
(
t
)
'List[
Type_Catalog
Brain[{}]]'
.
format
(
t
)
for
t
in
allowed_content_types_classes
)))
methods
.
append
(
method_template_template
.
format
(
decorator
=
''
,
method_name
=
method_name
,
method_args
=
"self"
,
# TODO
method_args
=
"self"
,
return_type
=
return_type
,
docstring
=
safe_docstring
(
getattr
(
getattr
(
temp_class
,
method_name
),
'__doc__'
,
None
))))
for
overload
in
overloads
:
return_type
=
'List[{}]'
.
format
(
safe_python_identifier
(
overload
))
if
method_name
==
'searchFolder'
:
return_type
=
'List[Brain[{}]]'
.
format
(
safe_python_identifier
(
overload
))
methods
.
append
(
method_template_template
.
format
(
decorator
=
'@overload'
,
method_name
=
method_name
,
method_args
=
"self, portal_type:Literal['{}']"
.
format
(
overload
),
# TODO
return_type
=
return_type
,
docstring
=
safe_docstring
(
getattr
(
getattr
(
temp_class
,
method_name
),
'__doc__'
,
None
))))
for
overload
in
overloads
:
methods
.
append
(
method_template_template
.
format
(
decorator
=
'@overload'
,
method_name
=
'newContent'
,
method_args
=
"self, portal_type:Literal['{}']"
.
format
(
overload
),
# TODO
return_type
=
safe_python_identifier
(
overload
),
docstring
=
safe_docstring
(
getattr
(
getattr
(
temp_class
,
method_name
),
'__doc__'
,
None
))))
methods
.
append
(
method_template_template
.
format
(
decorator
=
''
,
...
...
@@ -840,7 +863,11 @@ def TypeInformation_getStub(self, include_header=True):
# getattr, getitem and other Zope.OFS alais returns an instance of allowed content types.
# so that portal.person_module['1'] is a person
for
method_name
in
(
'__getattr__'
,
'__getitem__'
,
'_getOb'
,
'get'
):
for
method_name
in
(
'__getattr__'
,
'__getitem__'
,
'_getOb'
,
'get'
,):
methods
.
append
(
method_template_template
.
format
(
decorator
=
''
,
...
...
@@ -851,7 +878,8 @@ def TypeInformation_getStub(self, include_header=True):
for
identity_method
in
(
'getObject'
,
'asContext'
,):
'asContext'
,
'__of__'
,):
method
=
getattr
(
temp_class
,
identity_method
,
None
)
if
method
is
not
None
:
methods
.
append
(
...
...
@@ -868,40 +896,29 @@ def TypeInformation_getStub(self, include_header=True):
parent_class
=
safe_python_identifier
(
parent_class
.
__name__
))
base_classes
=
[
parent_class_alias
]
for
pc
in
temp_class
.
mro
():
#debug += "\n# parent class: -> from {} import {}".format( pc.__module__, pc.__name__)
if
pc
.
__module__
==
'erp5.accessor_holder.property_sheet'
:
#
XXX f
ake name for property sheets
#
F
ake name for property sheets
prefixed_class_name
=
'property_sheet_{}'
.
format
(
safe_python_identifier
(
pc
.
__name__
))
imports
.
a
ppen
d
(
imports
.
a
d
d
(
'from erp5.accessor_holder import {} as {}'
.
format
(
safe_python_identifier
(
pc
.
__name__
),
prefixed_class_name
))
base_classes
.
append
(
prefixed_class_name
)
#
XXX f
ake name for skins
#
F
ake name for skins
prefixed_class_name
=
'skins_tool_{}'
.
format
(
safe_python_identifier
(
pc
.
__name__
))
imports
.
a
ppen
d
(
imports
.
a
d
d
(
'from erp5.skins_tool import {} as {}'
.
format
(
safe_python_identifier
(
pc
.
__name__
),
prefixed_class_name
))
base_classes
.
append
(
prefixed_class_name
)
# everything can use ERP5Site_ skins
imports
.
a
ppen
d
(
'from erp5.skins_tool import ERP5Site as skins_tool_ERP5Site'
)
imports
.
a
d
d
(
'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}
...
...
@@ -926,7 +943,7 @@ def TypeInformation_getStub(self, include_header=True):
type_url
=
self
.
absolute_url
())
return
class_template
.
format
(
imports
=
"
\
n
"
.
join
(
imports
),
imports
=
"
\
n
"
.
join
(
sorted
(
imports
)
),
header
=
header
,
docstring
=
safe_docstring
(
docstring
),
class_name
=
safe_python_identifier
(
temp_class
.
__name__
),
...
...
@@ -977,12 +994,17 @@ def PropertySheet_getStub(self):
imports
=
[
'from typing import Optional, List, Any'
,
'from DateTime import DateTime'
,
'from erp5.portal_type import Type_AnyPortalType'
'from erp5.portal_type import Type_CatalogBrain'
,
'from erp5.portal_type import Type_AnyPortalType'
,
'from erp5.portal_type import Type_AnyPortalTypeList'
]
method_template_template
=
""" def {method_name}({method_args}) -> {return_type}:
\
n
{docstring}"""
# debug
methods
.
append
(
method_template_template
.
format
(
method_name
=
'cool{}'
.
format
(
safe_python_identifier
(
self
.
getId
())),
method_name
=
'reveal_property_sheet_{}'
.
format
(
safe_python_identifier
(
self
.
getId
())),
method_args
=
'self'
,
return_type
=
'str'
,
docstring
=
safe_docstring
(
"ahaha cool :)"
)))
...
...
@@ -997,8 +1019,8 @@ def PropertySheet_getStub(self):
'string'
:
'str'
,
'boolean'
:
'bool'
,
'data'
:
'bytes'
,
'date'
:
'DateTime.DateTime'
,
# XXX jedi does not understand DateTime dynamic name, so use "real name"
# XXX jedi does not understand DateTime dynamic name, so use "real name"
'date'
:
'DateTime.DateTime'
,
'int'
:
'int'
,
'long'
:
'int'
,
# ???
'lines'
:
'List[str]'
,
...
...
@@ -1110,14 +1132,14 @@ def PropertySheet_getStub(self):
method_name
=
'get{}Value'
.
format
(
convertToUpperCase
(
category_value
.
getId
())),
method_args
=
'self'
,
return_type
=
'
Optional[Type_AnyPortalType]
'
,
return_type
=
'
Type_AnyPortalType
'
,
docstring
=
docstring
))
methods
.
append
(
method_template_template
.
format
(
method_name
=
'get{}ValueList'
.
format
(
convertToUpperCase
(
category_value
.
getId
())),
method_args
=
'self'
,
return_type
=
'
List[Type_AnyPortalType]
'
,
return_type
=
'
Type_AnyPortalTypeList
'
,
docstring
=
docstring
))
methods
.
append
(
method_template_template
.
format
(
...
...
@@ -1130,14 +1152,14 @@ def PropertySheet_getStub(self):
method_template_template
.
format
(
method_name
=
'set{}Value'
.
format
(
convertToUpperCase
(
category_value
.
getId
())),
method_args
=
'self, value:
Category
'
,
method_args
=
'self, value:
Base
'
,
return_type
=
'None'
,
docstring
=
docstring
))
methods
.
append
(
method_template_template
.
format
(
method_name
=
'set{}ValueList'
.
format
(
convertToUpperCase
(
category_value
.
getId
())),
method_args
=
'self, value_list: List[
Category
]'
,
method_args
=
'self, value_list: List[
Base
]'
,
return_type
=
'None'
,
docstring
=
docstring
))
...
...
@@ -1158,7 +1180,6 @@ from Products.ERP5.ERP5Site import ERP5Site # pylint: disable=unused-import
def
ERP5Site_getPortalStub
(
self
):
# type: (ERP5Site) -> str
# TODO:
module_stub_template
=
textwrap
.
dedent
(
'''
@property
...
...
@@ -1182,68 +1203,36 @@ def ERP5Site_getPortalStub(self):
module_class_name
=
safe_python_identifier
(
m
.
getPortalType
())).
splitlines
())
el
if
m
.
getId
().
startswith
(
'portal_'
)
:
el
se
:
tool_class
=
safe_python_identifier
(
m
.
__class__
.
__name__
)
tool_import
=
'from {} import {}'
.
format
(
m
.
__class__
.
__module__
,
tool_class
,)
m
.
__class__
.
__module__
,
tool_class
)
if
m
.
getId
()
==
'portal_catalog'
:
tool_class
=
'ICatalogTool'
# XXX these I
prefix are stupid
tool_class
=
'ICatalogTool'
# XXX these I
-
prefix are stupid
tool_import
=
'from erp5.portal_type import ICatalogTool'
elif
m
.
getId
()
==
'portal_simulation'
:
tool_class
=
'ISimulationTool'
# XXX these I-prefix are stupid
tool_import
=
'from erp5.portal_type import ISimulationTool'
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 as ERP5Site_parent_ERP5Site
from erp5.skins_tool import ERP5Site as skins_tool_ERP5Site
class ERP5Site(ERP5Site_parent_ERP5Site, skins_tool_ERP5Site):
from erp5.skins_tool import Base as skins_tool_Base
class ERP5Site(ERP5Site_parent_ERP5Site, skins_tool_ERP5Site, skins_tool_Base):
{}
def getPortalObject(self):
return self
'''
).
format
(
'
\
n
'
.
join
(
source
))
def
CatalogTool_getStub
(
self
):
portal
=
self
.
getPortalObject
()
brain_class_list
=
[]
imports
=
[]
brain_template
=
textwrap
.
dedent
(
'''
\
from typing import TypeVar, Generic, List, Union
T = TypeVar('T')
class Brain(Generic[T]):
id: str
path: str
# XXX more depending on select_{list|dict} and getCatalogSearchResultKeys
def getObject(self) -> T:
...
'''
)
for
ti
in
portal
.
portal_types
.
contentValues
():
type_class
=
safe_python_identifier
(
ti
.
getId
())
# imports are already imported
# imports.append('from erp5.portal_type import {}'.format(type_class))
brain_class_list
.
append
(
'List[Brain[{}]]'
.
format
(
type_class
))
all_types
=
', '
.
join
(
brain_class_list
)
return
textwrap
.
dedent
(
'''
\
from Products.ERP5Catalog.Tool.ERP5CatalogTool import ERP5CatalogTool
{brain_template}
CatalogToolAllTypes = Union[{all_types}]
class ICatalogTool(ERP5CatalogTool):
def searchResults(self) -> CatalogToolAllTypes:
"""Search Catalog"""
def __call__(self) -> CatalogToolAllTypes:
"""Search Catalog"""
'''
).
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.
...
...
@@ -1254,7 +1243,10 @@ def ERP5Site_dumpModuleCode(self, component_or_script=None):
if
not
os
.
path
.
exists
(
path
):
os
.
mkdir
(
path
,
0o700
)
portal
=
self
.
getPortalObject
()
# type: 'erp5.portal_type.ERP5Site'
# this import is for type annotation, but pylint does not understand this.
import
erp5.portal_type
# pylint: disable=unused-variable
portal
=
self
.
getPortalObject
()
# type: erp5.portal_type.ERP5Site
module_dir
=
'/tmp/ahaha/erp5/'
# TODO
mkdir_p
(
module_dir
)
...
...
@@ -1268,10 +1260,10 @@ def ERP5Site_dumpModuleCode(self, component_or_script=None):
'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'
:
# portal types
all_portal_type_class_names
=
[]
with
open
(
os
.
path
.
join
(
module_dir
,
...
...
@@ -1280,6 +1272,7 @@ def ERP5Site_dumpModuleCode(self, component_or_script=None):
'w'
,)
as
module_f
:
for
ti
in
portal
.
portal_types
.
contentValues
():
class_name
=
safe_python_identifier
(
ti
.
getId
())
all_portal_type_class_names
.
append
(
class_name
)
module_f
.
write
(
'from .{class_name} import {class_name}
\
n
'
.
format
(
class_name
=
class_name
))
...
...
@@ -1293,8 +1286,42 @@ def ERP5Site_dumpModuleCode(self, component_or_script=None):
)
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')
# portal type groups ( useful ? used in Simulation Tool only )
portal_types_by_group
=
defaultdict
(
list
)
for
ti_for_group
in
portal
.
portal_types
.
contentValues
():
for
group
in
ti_for_group
.
getTypeGroupList
():
portal_types_by_group
[
group
].
append
(
safe_python_identifier
(
ti_for_group
.
getId
()))
for
group
,
portal_type_class_list
in
portal_types_by_group
.
items
():
group_class
=
'Group_{}'
.
format
(
group
)
module_f
.
write
(
'from .{} import {}
\
n
'
.
format
(
group_class
,
group_class
))
with
open
(
os
.
path
.
join
(
module_dir
,
module
,
'{}.pyi'
.
format
(
group_class
),),
'w'
,
)
as
group_f
:
group_f
.
write
(
textwrap
.
dedent
(
'''
{imports}
class {group_class}({bases}):
"""All portal types of group {group}.
"""
'''
).
format
(
imports
=
'
\
n
'
.
join
(
'from erp5.portal_type import {}'
.
format
(
portal_type_class
)
for
portal_type_class
in
portal_type_class_list
),
group_class
=
group_class
,
bases
=
', '
.
join
(
portal_type_class_list
),
group
=
group
))
# tools with extra type annotations
module_f
.
write
(
'from .ICatalogTool import ICatalogTool
\
n
'
)
with
open
(
os
.
path
.
join
(
...
...
@@ -1303,15 +1330,42 @@ def ERP5Site_dumpModuleCode(self, component_or_script=None):
'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
textwrap
.
dedent
(
'''
from Products.ERP5Catalog.Tool.ERP5CatalogTool import ERP5CatalogTool
# XXX CatalogTool itself has a portal type
from erp5.portal_type import Type_AnyPortalTypeCatalogBrainList
class ICatalogTool(ERP5CatalogTool):
def searchResults(self) -> Type_AnyPortalTypeCatalogBrainList:
"""Search Catalog"""
def __call__(self) -> Type_AnyPortalTypeCatalogBrainList:
"""Search Catalog"""
'''
))
module_f
.
write
(
'from .ISimulationTool import ISimulationTool
\
n
'
)
with
open
(
os
.
path
.
join
(
module_dir
,
module
,
'ISimulationTool.pyi'
,),
'w'
,)
as
portal_f
:
portal_f
.
write
(
textwrap
.
dedent
(
'''
from erp5.portal_type import SimulationTool
from erp5.portal_type import Type_AnyPortalTypeInventoryListBrainList
class ISimulationTool(SimulationTool):
def getInventoryList() -> Type_AnyPortalTypeInventoryListBrainList:
...
'''
))
# 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
,
...
...
@@ -1319,6 +1373,108 @@ def ERP5Site_dumpModuleCode(self, component_or_script=None):
'ERP5Site.pyi'
,),
'w'
,)
as
portal_f
:
portal_f
.
write
(
ERP5Site_getPortalStub
(
self
.
getPortalObject
()))
# some type helpers
module_f
.
write
(
'from .Type_CatalogBrain import Type_CatalogBrain
\
n
'
)
with
open
(
os
.
path
.
join
(
module_dir
,
module
,
'Type_CatalogBrain.pyi'
,),
'w'
,)
as
catalog_brain_f
:
catalog_brain_f
.
write
(
textwrap
.
dedent
(
'''
from typing import TypeVar, Generic
T = TypeVar('T')
class Type_CatalogBrain(Generic[T]):
id: str
path: str
def getObject(self) -> T:
...
'''
))
module_f
.
write
(
'from .Type_InventoryListBrain import Type_InventoryListBrain
\
n
'
)
with
open
(
os
.
path
.
join
(
module_dir
,
module
,
'Type_InventoryListBrain.pyi'
,),
'w'
,
)
as
catalog_brain_f
:
catalog_brain_f
.
write
(
textwrap
.
dedent
(
'''
from typing import TypeVar, Generic
from erp5.component.extension.InventoryBrain import InventoryListBrain
from DateTime import DateTime.DateTime as DateTime
from erp5.portal_type import Group_node
from erp5.portal_type import Group_resource
T = TypeVar('T')
class Type_InventoryListBrain(Generic[T], InventoryListBrain):
node_uid: int
mirror_node_uid: int
section_uid: int
mirror_section_uid: int
function_uid: int
project_uid: int
function_uid: int
funding_uid: int
ledger_uid: int
payment_request_uid: int
node_value: Group_node
mirror_node_value: Group_node
section_value: Group_node
mirror_section_value: Group_node
resource_value: Group_resource
date: DateTime
mirror_date: DateTime
variation_text: str
sub_variation_text: str
simulation_state: str
inventory: float
total_price: float
path: str
stock_uid: uid
def getObject(self) -> T:
...
'''
))
module_f
.
write
(
'from typing import List, Union
\
n
'
)
module_f
.
write
(
'Type_AnyPortalType = Union[
\
n
{}]
\
n
'
.
format
(
',
\
n
'
.
join
(
'{}'
.
format
(
portal_type_class
)
for
portal_type_class
in
all_portal_type_class_names
),
))
module_f
.
write
(
'Type_AnyPortalTypeList = Union[
\
n
{}]
\
n
'
.
format
(
',
\
n
'
.
join
(
'List[{}]'
.
format
(
portal_type_class
)
for
portal_type_class
in
all_portal_type_class_names
)))
module_f
.
write
(
'Type_AnyPortalTypeCatalogBrainList = Union[
\
n
{}]
\
n
'
.
format
(
',
\
n
'
.
join
(
'List[Type_CatalogBrain[{}]]'
.
format
(
portal_type_class
)
for
portal_type_class
in
all_portal_type_class_names
),
))
module_f
.
write
(
'Type_AnyPortalTypeInventoryListBrainList = Union[
\
n
{}]
\
n
'
.
format
(
',
\
n
'
.
join
(
'List[Type_InventoryListBrain[{}]]'
.
format
(
portal_type_class
)
for
portal_type_class
in
all_portal_type_class_names
),
))
elif
module
==
'accessor_holder'
:
# TODO: real path is accessor_holder.something !?
with
open
(
...
...
@@ -1359,6 +1515,7 @@ def ERP5Site_dumpModuleCode(self, component_or_script=None):
skins_tool
,
class_name
,).
encode
(
'utf-8'
))
elif
module
==
'component'
:
# TODO: component versions ?
module_to_component_portal_type_mapping
=
{
'test'
:
'Test Component'
,
'document'
:
'Document Component'
,
...
...
bt5/erp5_monaco_editor/ExtensionTemplateItem/portal_components/extension.erp5.Jedi.xml
View file @
0766d41f
...
...
@@ -45,7 +45,13 @@
<item>
<key>
<string>
text_content_warning_message
</string>
</key>
<value>
<tuple/>
<tuple>
<string>
W: 58, 2: Reimport \'ContextSet\' (imported line 49) (reimported)
</string>
<string>
W: 81, 8: Unreachable code (unreachable)
</string>
<string>
W: 76, 8: Unused variable \'annotation_classes\' (unused-variable)
</string>
<string>
W:158, 8: Unreachable code (unreachable)
</string>
<string>
W:186, 16: Unused variable \'filtered\' (unused-variable)
</string>
</tuple>
</value>
</item>
<item>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment