Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix graph crash with partial conditions #239

Merged
merged 5 commits into from Jul 24, 2017

Conversation

Synss
Copy link
Contributor

@Synss Synss commented Jul 23, 2017

Graph.rep() assumes that every callable has a __name__ attribute.
However, callables like functools.partial and other classes with a
__call__() method have no __name__. This patch modifies
Graph.rep() to first check whether the condition is a string and
return it, then try to return f.__name__ (that is the current
behavior) and if that fails, then it calls str() on the argument.

str() should never fail and it is now the responsibility of the user
to return something meaningful from __str__().

Graph.rep() assumes that every callable has a `__name__` attribute.
However, callables like `functools.partial` and other classes with a
`__call__()` method have no `__name__`.  This patch modifies
`Graph.rep()` to first check whether the condition is a string and
return it, then try to return `f.__name__` (that is the current
behavior) and if that fails, then it calls `str()` on the argument.

`str()` should never fail and it is now the responsibility of the user
to return something meaningful from `__str__()`.
@coveralls
Copy link

coveralls commented Jul 23, 2017

Coverage Status

Coverage decreased (-0.07%) to 99.778% when pulling 1ca302e on Synss:graphing-nameless-callable into 9f7cbe3 on pytransitions:master.

@Synss
Copy link
Contributor Author

Synss commented Jul 23, 2017

If you like, I can explicitly handle functools.partial and return "check(True)" for

def check(result):
    return result

... conditions=partial(check, True)...

@aleneum
Copy link
Member

aleneum commented Jul 23, 2017

Partials for condition checks haven't been tested anywhere else yet. If you call fly and make sure that both conditions are checked this would also test if partials are handled correctly. Just in case someone introduces something later on which breaks this compatibility. Like:

# [...]
m = self.machine_cls(states=['A', 'B', 'C'], initial= 'A', show_conditions=True,
                     title='a test')
m.add_state({'name': 'E'})
m.add_transition(trigger='fly', source='A', dest='B',
                 conditions=Check(False))
m.add_transition(trigger='fly', source='A', dest='C',
                 unless=functools.partial(check, False))
m.fly()
self.assertTrue(m.is_C())
# [...]

@Synss
Copy link
Contributor Author

Synss commented Jul 23, 2017

I could test partial conditions in test_core, make Graph.rep() a staticmethod (it does not use self anyway) and test Graph.rep() directly. Is that OK?

@aleneum
Copy link
Member

aleneum commented Jul 23, 2017

I could test partial conditions in test_core, make Graph.rep() a staticmethod (it does not use self anyway) and test Graph.rep() directly. Is that OK?

Sounds good. Currently, the 'coverage problem' is, that test_anonymous_callable_conditions does not actually makes use of Check and the partial function check though (see coverage report). So yeah, testing partials for condition checks in core does make sense as well as making rep a module function (does not even have to be part of Graph) but it does not solve the issue of unused constructs in the test itself.
I know its not a big issue but I would prefer if this could be solved in a meaningful/contributing way.

As discussed in pytransitions#239:
 - `rep()` is now a free function
 - `rep()` pretty-prints partial functions
 - `rep()` calls `str()` on its argument when everything else fails

Also, the following cases are now tested:
 - `rep()` with a string argument returns the string
 - `rep()` with a function returns the name of the function
 - `rep()` with a partial function returns name + args + kwargs
 - `rep()` with anything else calls `str()`
@coveralls
Copy link

coveralls commented Jul 23, 2017

Coverage Status

Coverage decreased (-1.5%) to 98.32% when pulling ef9f4df on Synss:graphing-nameless-callable into 9f7cbe3 on pytransitions:master.

@Synss
Copy link
Contributor Author

Synss commented Jul 23, 2017

Tests passed on my machine with 2.7.13, I'll try again...

The CI build has shown that `partial().keyword` may be None
in Python 2.7--3.4.
@coveralls
Copy link

coveralls commented Jul 23, 2017

Coverage Status

Coverage decreased (-0.2%) to 99.673% when pulling 235bfa0 on Synss:graphing-nameless-callable into 9f7cbe3 on pytransitions:master.

@aleneum aleneum merged commit 33c15d0 into pytransitions:master Jul 24, 2017
@aleneum
Copy link
Member

aleneum commented Jul 24, 2017

Looks good to me, one function name was misspelled, thats why the test hadnt been executed.
Thanks!

@Synss Synss deleted the graphing-nameless-callable branch July 24, 2017 17:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants