Skip to content

Commit

Permalink
Merge branch 'master' into fix/typings
Browse files Browse the repository at this point in the history
  • Loading branch information
masfaraud committed Apr 14, 2022
2 parents 53fcc7c + 71365f1 commit ae9d2af
Show file tree
Hide file tree
Showing 14 changed files with 189 additions and 140 deletions.
2 changes: 1 addition & 1 deletion .drone.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ steps:
- git fetch --tags
- python setup.py install
- python scripts/dessia_object.py
- pip install sphinx sphinx-rtd-theme coverage plot-data
- pip install sphinx sphinx-rtd-theme coverage plot-data volmdlr
- cd doc
- make html
- cd ../tests
Expand Down
2 changes: 1 addition & 1 deletion .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ function-naming-style=snake_case
#function-rgx=

# Good variable names which should always be accepted, separated by a comma.
good-names=i,j,k,n,ie,x,xi,x0,
good-names=i,j,k,n,ie,x,xi,x0,v1,v2,
ax,
_

Expand Down
43 changes: 27 additions & 16 deletions code_pylint.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
'''
Read pylint errors to see if number of errors does not exceed specified limits
v1.1
v1.2
Changes:
v1.1: move imports to top
v1.2: limit to 100 message to avoid overflow, global note check at end, ratchet effects
'''


import os
import sys
from pylint import __version__
from pylint.lint import Run

MIN_NOTE = 9.1
MIN_NOTE = 9.17

UNWATCHED_ERRORS = ['fixme', 'trailing-whitespace', 'import-error']

MAX_ERROR_BY_TYPE = {
'protected-access': 26,
'invalid-name': 18,
'invalid-name': 8,
'consider-using-f-string': 10,
'no-else-return': 17,
'no-else-return': 4,
'arguments-differ': 12,
'no-member': 1,
'too-many-locals': 14,
Expand All @@ -28,7 +31,7 @@
'unused-argument': 6,
'cyclic-import': 11,
'no-self-use': 6,
'unused-variable': 6,
'unused-variable': 1,
'trailing-whitespace': 11,
'empty-docstring': 7,
'missing-module-docstring': 9,
Expand Down Expand Up @@ -84,6 +87,8 @@
'unsubscriptable-object': 0
}

print('pylint version: ', __version__)

f = open(os.devnull, 'w')

old_stdout = sys.stdout
Expand All @@ -101,18 +106,13 @@
pylint_note = results.linter.stats['global_note']
PYLINT_OBJECT_STATS = False

print('Pylint note: ', pylint_note)
assert pylint_note >= MIN_NOTE
print('You can increase MIN_NOTE in pylint to {} (actual: {})'.format(pylint_note,
MIN_NOTE))


def extract_messages_by_type(type_):
return [m for m in results.linter.reporter.messages if m.symbol == type_]


# uncontrolled_errors = {}
error_detected = False
error_over_ratchet_limit = False

if PYLINT_OBJECT_STATS:
stats_by_msg = results.linter.stats.by_msg
Expand All @@ -126,21 +126,32 @@ def extract_messages_by_type(type_):
else:
max_errors = 0

# if number_errors < max_errors - RATCHET_ERRORS:
# error_over_ratchet_limit = True

if number_errors > max_errors:
error_detected = True
print('Fix some {} errors: {}/{}'.format(error_type,
number_errors,
max_errors))
for message in extract_messages_by_type(error_type):
for message in extract_messages_by_type(error_type)[:30]:
print('{} line {}: {}'.format(message.path, message.line, message.msg))
elif number_errors < max_errors:
print('You can lower number of {} to {} (actual {})'.format(
error_type, number_errors, max_errors))

# uncontrolled_errors[error_type] = number_errors

# if uncontrolled_errors:
# print('Uncontrolled errors', uncontrolled_errors)

if error_detected:
raise RuntimeError('Too many errors\nRun pylint dessia_common to get the errors')

if error_over_ratchet_limit:
raise RuntimeError('Please lower the error limits in code_pylint.py MAX_ERROR_BY_TYPE according to warnings above')

print('Pylint note: ', pylint_note)
# if pylint_note > MIN_NOTE + RATCHET_NOTE:
# raise ValueError(f'MIN_NOTE in code_pylint.py is too low, increase to at least {MIN_NOTE + RATCHET_NOTE}, max {pylint_note}')
if pylint_note < MIN_NOTE:
raise ValueError(f'Pylint not is too low: {pylint_note}, expected {MIN_NOTE}')

print('You can increase MIN_NOTE in pylint to {} (actual: {})'.format(pylint_note,
MIN_NOTE))
81 changes: 50 additions & 31 deletions dessia_common/breakdown.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,46 +5,65 @@
"""

import sys
from ast import literal_eval
import collections
import numpy as npy

import dessia_common
from dessia_common.utils.types import is_sequence


def get_in_object_from_path(object_, path):
segments = path.lstrip('#/').split('/')
if isinstance(object_, dict):
try:
element = object_[segments[0]]
except KeyError:
msg = f'Cannot get in dict path {path}: end up @ {segments[0]}. Dict keys: {object_.keys()}'
raise RuntimeError(msg)
else:
element = getattr(object_, segments[0])

for segment in segments[1:]:
if is_sequence(element):
element = element[int(segment)]
elif isinstance(element, dict): # A dict?
if segment in element:
element = element[segment]
def attrmethod_getter(object_, attr_methods):
"""
Float with . in attributes are not handled
"""
# TODO: escape . inside ()
for segment in attr_methods.split('.'):
if '(' in segment:
method, _, attributes = segment.partition('(')
attributes = attributes[:-1]
if attributes:
object_ = getattr(object_, method)(literal_eval(attributes))
else:
try:
key = int(segment)
except ValueError:
# should be a tuple
key = []
for subsegment in segment.strip('()').replace(' ', '').split(','):
try:
subkey = int(subsegment)
except ValueError:
subkey = subsegment
key.append(subkey)
key = tuple(key)
element = element[key]
object_ = getattr(object_, method)()
else:
element = getattr(element, segment)
object_ = getattr(object_, segment)
return object_


def extract_from_object(object_, segment):
if is_sequence(object_):
return object_[int(segment)]

if isinstance(object_, dict):
if segment in object_:
return object_[segment]

try:
return object_[int(segment)]
except ValueError:
# should be a tuple
if segment.startswith('(') and segment.endswith(')') and ',' in segment:
key = []
for subsegment in segment.strip('()').replace(' ', '').split(','):
try:
subkey = int(subsegment)
except ValueError:
subkey = subsegment
key.append(subkey)
return object_[tuple(key)]
# else:
raise NotImplementedError(f'Cannot extract segment {segment} from object {object_}')

# Finally, it is a regular object
return getattr(object_, segment)


def get_in_object_from_path(object_, path):
segments = path.lstrip('#/').split('/')
element = object_
for segment in segments:
element = extract_from_object(element, segment)

return element

Expand Down
17 changes: 10 additions & 7 deletions dessia_common/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
from dessia_common.typings import JsonSerializable
from dessia_common import templates
from dessia_common.displays import DisplayObject, DisplaySetting
from dessia_common.breakdown import attrmethod_getter


_FORBIDDEN_ARGNAMES = ['self', 'cls', 'progress_callback', 'return']
Expand Down Expand Up @@ -562,7 +563,7 @@ def _display_from_selector(self, selector: str, **kwargs):
if display_setting.selector == selector:
track = ''
try:
data = attrgetter(display_setting.method)(self)(**display_setting.arguments)
data = attrmethod_getter(self, display_setting.method)(**display_setting.arguments)
except:
data = None
track = tb.format_exc()
Expand Down Expand Up @@ -647,7 +648,9 @@ def display_settings():
Returns a list of json describing how to call subdisplays
"""
display_settings = DessiaObject.display_settings()
display_settings.append(DisplaySetting(selector='cad', type_='babylon_data', method='volmdlr_volume_model'))
display_settings.append(DisplaySetting(selector='cad', type_='babylon_data',
method='volmdlr_volume_model().babylon_data',
serialize_data=True))
return display_settings

def volmdlr_primitives(self):
Expand Down Expand Up @@ -690,11 +693,11 @@ def to_stl(self, filepath):
"""
return self.volmdlr_volume_model().to_stl(filepath=filepath)

def _displays(self, **kwargs):
"""
Compute the list of displays
"""
return DessiaObject._displays(self, **kwargs)
# def _displays(self, **kwargs):
# """
# Compute the list of displays
# """
# return DessiaObject._displays(self, **kwargs)

def babylonjs(self, use_cdn=True, debug=False, **kwargs):
"""
Expand Down
11 changes: 5 additions & 6 deletions dessia_common/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,11 @@ def cut_tree_final_branches(graph: nx.DiGraph):
def explore_tree_from_leaves(graph: nx.DiGraph):
exploration_order = []
explored = {n: False for n in graph.nodes}
nn = graph.number_of_nodes()
# print('nn', nn)
number_nodes = graph.number_of_nodes()
successors = {}

ns = 0
while nn:
# ns = 0
while number_nodes:
found_node = False
# Finding a starting node
for node in graph.nodes:
Expand All @@ -51,7 +50,7 @@ def explore_tree_from_leaves(graph: nx.DiGraph):
else:
node_successors = list(graph.successors(node))
successors[node] = node_successors
ns += 1
# ns += 1

for out_node in node_successors:
if not explored[out_node]:
Expand All @@ -62,7 +61,7 @@ def explore_tree_from_leaves(graph: nx.DiGraph):
# Mark explored
explored[node] = True
exploration_order.append(node)
nn -= 1
number_nodes -= 1
found_node = True
break
if not found_node:
Expand Down
26 changes: 12 additions & 14 deletions dessia_common/utils/copy.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,23 @@ def deepcopy_value(value, memo):
if isinstance(value, type) or is_typing(value): # For class
return value

elif isinstance(value, (float, int, str)):
if isinstance(value, (float, int, str)):
copied_value = value
return copied_value

elif value is None:
if value is None:
return None

elif value.__class__.__name__ in ['Point2D', 'Point3D',
'Vector2D', 'Vector3D']:
if value.__class__.__name__ in ['Point2D', 'Point3D',
'Vector2D', 'Vector3D']:
try:
copied_value = value.copy(deep=True, memo=memo)
except TypeError:
warnings.warn(f'{value.__class__.__name__}.copy() does not implement deep and memo arguments')
copied_value = value.copy()
return copied_value

elif isinstance(value, dc.DessiaObject):
if isinstance(value, dc.DessiaObject):
memo_value = search_memo(value, memo)
if memo_value is not None:
return memo_value
Expand All @@ -42,10 +42,10 @@ def deepcopy_value(value, memo):
memo[value] = copied_value
return copied_value

elif isinstance(value, (dessia_common.files.BinaryFile, dessia_common.files.StringFile)):
if isinstance(value, (dessia_common.files.BinaryFile, dessia_common.files.StringFile)):
return value.copy()

elif hasattr(value, '__deepcopy__'):
if hasattr(value, '__deepcopy__'):
memo_value = search_memo(value, memo)
if memo_value is not None:
return memo_value
Expand All @@ -57,15 +57,13 @@ def deepcopy_value(value, memo):
memo[value] = copied_value
return copied_value

else:
if is_sequence(value):
return deepcopy_sequence(value, memo)
if is_sequence(value):
return deepcopy_sequence(value, memo)

elif isinstance(value, dict):
return deepcopy_dict(value, memo)
if isinstance(value, dict):
return deepcopy_dict(value, memo)

else:
raise NotImplementedError(f'unhandle type for copy: {value} of type {value.__class__}')
raise NotImplementedError(f'unhandle type for copy: {value} of type {value.__class__}')


def deepcopy_dict(dict_value, memo):
Expand Down
Loading

0 comments on commit ae9d2af

Please sign in to comment.