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

Contracts does not stop execution immediately having found an unexpected OR null input #65

Open
AldarisPale opened this issue May 17, 2018 · 1 comment

Comments

@AldarisPale
Copy link

Hi.

I think I have stumbled upon a bug or if not a bug then atleast an counterintuitive feature.
Consider the following code:

#!/usr/bin/python3
# -*- coding: utf8 -*-

from contracts import contract

class ObjectThatWillSeeDeathBeforeBeingBorn(object):

    @contract
    def __init__(self, TestingString: 'str[>0]'):
        print('String given as parameter: %s.' % TestingString)
        print('ID of ObjectThatWillSeeDeathBeforeBeingBorn: %s.' % id(self))

    def __del__(self):
        print('ID of object being killed: %s.' % id(self))


NewObjectForTestingTheBug = ObjectThatWillSeeDeathBeforeBeingBorn()

When executed as listed above we will get this as output:

$ python3 contracts_possible_bug.py 
ID of object being killed: 139899159500840.
Traceback (most recent call last):
  File "contracts_possible_bug.py", line 17, in <module>
    NewObjectForTestingTheBug = ObjectThatWillSeeDeathBeforeBeingBorn()
TypeError: __init__() missing 1 required positional argument: 'TestingString'

The notable thing here is that we don't get any output from init but we do get output from del.

If we modify the last line so it looks like this:
NewObjectForTestingTheBug = ObjectThatWillSeeDeathBeforeBeingBorn(42)
the output looks like this:

$ python3 contracts_possible_bug.py 
Traceback (most recent call last):
  File "contracts_possible_bug.py", line 17, in <module>
    NewObjectForTestingTheBug = ObjectThatWillSeeDeathBeforeBeingBorn(42)
  File "<decorator-gen-1>", line 2, in __init__
  File "/usr/local/lib/python3.5/dist-packages/contracts/main.py", line 260, in contracts_checker
    raise e
  File "/usr/local/lib/python3.5/dist-packages/contracts/main.py", line 255, in contracts_checker
    accepts_parsed[arg]._check_contract(context, bound[arg], silent=False)
  File "/usr/local/lib/python3.5/dist-packages/contracts/interface.py", line 439, in _check_contract
    self.check_contract(context, value, silent)
  File "/usr/local/lib/python3.5/dist-packages/contracts/library/strings.py", line 19, in check_contract
    value=value, context=context)
contracts.interface.ContractNotRespected: Breach for argument 'TestingString' to ObjectThatWillSeeDeathBeforeBeingBorn:__init__().
Expected a string, got 'int'.
checking: str[>0]   for value: Instance of <class 'int'>: 42   
Variables bound in inner context:
- self: Instance of <class '__main__.ObjectThatWillSeeDeathBeforeBeingBorn'>: <__main__.ObjectThatWillSeeDeathBeforeBeingBorn object at 0x7fe... [clip]
ID of object being killed: 140638653347880.

I had expected that when a breach of contract has been found everything grinds to a halt but we can see that in both cases code still gets executed.

Last but not least, thank You for Your great work on contracts!

@AldarisPale
Copy link
Author

Hi again.

After getting deeper into python and learning that __del__ is a bad idea anyway I have refactored the code:

#!/usr/bin/python3
# -*- coding: utf8 -*-

from contracts import contract

class ObjectThatWillSeeDeathBeforeBeingBorn(object):

    @contract
    def __init__(self, TestingString: 'str[>0]'):
        print('String given as parameter: %s.' % TestingString)
        print('ID of ObjectThatWillSeeDeathBeforeBeingBorn: %s.' % id(self))

    def __enter__(self):
        pass

    def __exit__(self, type, value, tb):
        print('ID of object being killed: %s.' % id(self))


with ObjectThatWillSeeDeathBeforeBeingBorn() as NewObjectForTestingTheBug:
    pass

we now get:

$ python3 contracts_possible_bug.py 
Traceback (most recent call last):
  File "contracts_possible_bug.py", line 20, in <module>
    with ObjectThatWillSeeDeathBeforeBeingBorn() as NewObjectForTestingTheBug:
TypeError: __init__() missing 1 required positional argument: 'TestingString'

Please note, that code in __exit__ did not get executed.

Lets try initating the object with a wrong type:
with ObjectThatWillSeeDeathBeforeBeingBorn(42) as NewObjectForTestingTheBug:

Result:

$ python3 contracts_possible_bug.py 
Traceback (most recent call last):
  File "contracts_possible_bug.py", line 20, in <module>
    with ObjectThatWillSeeDeathBeforeBeingBorn(42) as NewObjectForTestingTheBug:
  File "<decorator-gen-1>", line 2, in __init__
  File "/usr/local/lib/python3.5/dist-packages/contracts/main.py", line 260, in contracts_checker
    raise e
  File "/usr/local/lib/python3.5/dist-packages/contracts/main.py", line 255, in contracts_checker
    accepts_parsed[arg]._check_contract(context, bound[arg], silent=False)
  File "/usr/local/lib/python3.5/dist-packages/contracts/interface.py", line 439, in _check_contract
    self.check_contract(context, value, silent)
  File "/usr/local/lib/python3.5/dist-packages/contracts/library/strings.py", line 19, in check_contract
    value=value, context=context)
contracts.interface.ContractNotRespected: Breach for argument 'TestingString' to ObjectThatWillSeeDeathBeforeBeingBorn:__init__().
Expected a string, got 'int'.
checking: str[>0]   for value: Instance of <class 'int'>: 42   
Variables bound in inner context:
- self: Instance of <class '__main__.ObjectThatWillSeeDeathBeforeBeingBorn'>: <__main__.ObjectThatWillSeeDeathBeforeBeingBorn object at 0x7f6... [clip]

Now let's try with the correct type:
with ObjectThatWillSeeDeathBeforeBeingBorn("42") as NewObjectForTestingTheBug:

Result:

$ python3 contracts_possible_bug.py 
String given as parameter: 42.
ID of ObjectThatWillSeeDeathBeforeBeingBorn: 139824865924656.
ID of object being killed: 139824865924656.

So the lesson here is not to use __del__ when dealing with objects.

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

No branches or pull requests

1 participant