I'm porting some code from IronPython to CPython (3.8) + Python.NET and I have a custom class that's broken in an odd way: it gives me an AttributeError even though the member is present in __dict__. This class derives from uuid.UUID to add support to BLE short UUIDs:
import uuid
class UUID(uuid.UUID):
"""Represents a more general version of the uuid.UUID, so we can have both
regular and short UUIDs.
"""
def __init__(self, hex=None, bytes=None, is_short=False, **kwargs):
try:
super(UUID, self).__init__(hex=hex, bytes=bytes, **kwargs)
self.__dict__['is_short'] = False
except ValueError as val_ex:
if hex is None or len(hex) != 4:
raise val_ex
# Remove braces GUIDs
hex_digits = hex.strip('{}').replace('-', '')
# remove RFC 4122 URN's 'urn:uuid:deadbeef-1234-fedc-5678-deadbeefaaaa'
hex_digits = hex_digits.replace('uuid:', '').replace('urn:', '')
if len(hex_digits) != 4:
raise ValueError('badly formed hexadecimal UUID string')
self.__dict__['int'] = int(hex, 16)
self.__dict__['is_short'] = True
if is_short is True:
self.__dict__['is_short'] = True
def __str__(self):
if self.is_short:
hex = '%04x' % self.int
return '%s' % (hex[:4])
else:
return super(UUID, self).__str__()
And I'm using it like so:
class Test_UUID(unittest.TestCase):
def test_create(self):
x = pybletestlibrary.UUID("1800")
pprint(x.__dict__)
print(x)
The above code yields:
{'int': 6144, 'is_short': True}
E
======================================================================
ERROR: test_create (__main__.Test_UUID)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test\test_pybletestlibrary.py", line 27, in test_create
print(x)
File "my_uuid.py", line 66, in __str__
hex = '%04x' % self.int
AttributeError: int
----------------------------------------------------------------------
Ran 1 test in 0.003s
FAILED (errors=1)
I can't use setattr() because uuid.UUID is immutable. I don't understand why the AttributeError as int is present in __dict__. Any suggestions?
Thanks!!
Here's what I mean:
import uuid
class UUID(uuid.UUID):
"""Represents a more general version of the uuid.UUID, so we can have both
regular and short UUIDs.
"""
def __init__(self, hex=None, bytes=None, is_short=False, **kwargs):
self.is_short = False
if len(hex) == 4:
self.is_short = True
hex = "0000"+hex+"-0000-1000-8000-00805f9b34fb"
super(UUID, self).__init__(hex=hex, bytes=bytes, **kwargs)
def __str__(self):
val = super(UUID, self).__str__()
if self.is_short:
return val[4:8]
else:
return val
You might have to override some of the other attributes, too.
Related
I want to design an error in python.
Here is my code
class TwitchException(Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return "The input ", self.value ," is not a valid reaction."
valid_reactions = ["Poggers", "4Head","SMOrc", "TheIlluminati"]
def react(reaction):
"""
>>> react("Poggers")
Poggers
>>> react("Hello")
Traceback (most recent call last):
...
TwitchException: The input Hello is not a valid reaction.
>>> react("SMOrc")
SMOrc
"""
try:
if reaction in valid_reactions:
print(reaction)
except TwitchException:
raise TwitchException(reaction)
Help!!! I am not sure how to fulfill the second doctest.
You want to raise the exception in the else clause of the if statement; there is no need for a try statement here.
def react(reaction):
if reaction in valid_reactions:
print(reaction)
else:
raise TwitchException(reaction)
This is, however, typically written as
def react(reaction):
if reaction not in valid_reactions:
raise TwitchException(reaction)
print(reaction)
Method __str__ must return a string. Yours returns a tuple:
def __str__(self):
return "The input ", self.value ," is not a valid reaction."
It should be:
def __str__(self):
return "The input {} is not a valid reaction.".format(self.value)
I can't seem to figure out what is causing the below attribute error. I am really not sure what I am doing wrong here. Any help would be great!
Traceback (most recent call last):
File "project2.py", line 140, in
movie_writter.writerow([movie.title, movie.author, movie.itunes_id, movie.itunes_URL, movie.length])
AttributeError: 'Movie' object has no attribute 'length'
class Movie(Media):
def __init__(self, diction_file):
Media.__init__(self, diction_file)
self.rating = diction_file['contentAdvisoryRating']
self.genre = diction_file['primaryGenreName']
try:
self.track_time = diction_file['trackTimeMillis']
except:
self.track_time = 0
try:
self.description = diction_file['longDescription']
except:
self.description = None
def __len__(self):
return self.track_time/1000
def title_words_num(self):
if self.description != None:
return len(self.description.split())
else:
return 0
movie_list = [Movie(diction_file) for diction_file in movie_samples]
with open('movies.csv', 'w', newline = '') as movies_csv:
movie_writter = csv.writer(movies_csv, delimiter=',')
movie_writter.writerow(["Title", "Artist", "ID", "URL", "Length"])
for movie in movie_list:
movie_writter.writerow([movie.title, movie.author, movie.itunes_id, movie.itunes_URL, movie.length])
movies_csv.close()
It means what it means: the object has no attribute length. You probably meant len(movie), which maps to __len__ magic method.
Also, keep in mind, this is not the best use for the __len__ method. It is supposed to be used for collections/containers of any kind, to show the amout of items inside. The movie duration is not an amount of things.
Following a remark here: How to define a new string formatter, I tried subclassing string.Formatter. Here is what I've done. Unfortunately I seem to have broken it in the process
import string
from math import floor, log10
class CustFormatter(string.Formatter):
"Defines special formatting"
def __init__(self):
super(CustFormatter, self).__init__()
def powerise10(self, x):
if x == 0: return 0, 0
Neg = x < 0
if Neg: x = -x
a = 1.0 * x / 10**(floor(log10(x)))
b = int(floor(log10(x)))
if Neg: a = -a
return a, b
def eng(self, x):
a, b = self.powerise10(x)
if -3 < b < 3: return "%.4g" % x
a = a * 10**(b%3)
b = b - b%3
return "%.4g*10^%s" % (a, b)
def format_field(self, value, format_string):
# handle an invalid format
if format_string == "i":
return self.eng(value)
else:
return super(CustFormatter,self).format_field(value, format_string)
fmt = CustFormatter()
print('{}'.format(0.055412))
print(fmt.format("{0:i} ", 55654654231654))
print(fmt.format("{} ", 0.00254641))
As if as in the last line, I don't refer to the variables by position, I get a KeyError. It is obviously expecting a key which is optional in the original class but I don't understand why and I am not sure what I've done wrong.
str.format does auto numbering, while string.Formatter does not.
Modifying __init__ and overriding get_value will do the trick.
def __init__(self):
super(CustFormatter, self).__init__()
self.last_number = 0
def get_value(self, key, args, kwargs):
if key == '':
key = self.last_number
self.last_number += 1
return super(CustFormatter, self).get_value(key, args, kwargs)
BTW, above code does not strictly mimic str.format behavior. str.format complains if we mix auto numbering with manual number, but above does not.
>>> '{} {1}'.format(1, 2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: cannot switch from automatic field numbering to manual field specification
>>> '{0} {}'.format(1, 2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: cannot switch from manual field specification to automatic field numbering
Good news: you have not done anything wrong.
Bad news: that's how string.Formatter behaves, it does not support {}-like positional format. So, the last call will fail even without any subclassing. Good news: that's can be fixed by overriding the parse method:
import string
class CF(string.Formatter):
def parse(self, s):
position = 0
for lit, name, spec, conv in super(CF, self).parse(s):
if not name:
name = str(position)
position += 1
yield lit, name, spec, conv
Bad news... Ah, no, that's basically it:
>>> CF().format('{} {}!', 'Hello', 'world')
'Hello world!'
import unittest
import filterList
class TestFilterList(unittest.TestCase):
""" docstring for TestFilterList
"""
def setUp(self):
self._filterby = 'B'
def test_checkListItem(self):
self.flObj = filterList.FilterList(['hello', 'Boy', 1], self._filterby)
self.assertRaises(filterList.ItemNotString, self.flObj.checkListItem)
def test_filterList(self):
self.flObj = filterList.FilterList(['hello', 'Boy'], self._filterby)
self.assertEquals(['Boy'], self.flObj.filterList())
if __name__ == '__main__':
unittest.main()
My above test test_checkListItem() fails , for the below filterList module:
import sys
import ast
class ItemNotString(Exception):
pass
class FilterList(object):
"""docstring for FilterList
"""
def __init__(self, lst, filterby):
super(FilterList, self).__init__()
self.lst = lst
self._filter = filterby
self.checkListItem()
def checkListItem(self):
for index, item in enumerate(self.lst):
if type(item) == str:
continue
else:
raise ItemNotString("%i item '%s' is not of type string" % (index+1, item))
print self.filterList()
return True
def filterList(self):
filteredList = []
for eachItem in self.lst:
if eachItem.startswith(self._filter):
filteredList.append(eachItem)
return filteredList
if __name__ == "__main__":
try:
filterby = sys.argv[2]
except IndexError:
filterby = 'H'
flObj = FilterList(ast.literal_eval(sys.argv[1]), filterby)
#flObj.checkListItem()
Why does the test fail with the error:
======================================================================
ERROR: test_checkListItem (__main__.TestFilterList)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_filterList.py", line 13, in test_checkListItem
self.flObj = filterList.FilterList(['hello', 'Boy', 1], self._filterby)
File "/Users/sanjeevkumar/Development/python/filterList.py", line 16, in __init__
self.checkListItem()
File "/Users/sanjeevkumar/Development/python/filterList.py", line 23, in checkListItem
raise ItemNotString("%i item '%s' is not of type string" % (index+1, item))
ItemNotString: 3 item '1' is not of type string
----------------------------------------------------------------------
Ran 2 tests in 0.000s
FAILED (errors=1)
Also, is the approach of the filterList module correct ?
The exception is not being caught by your assertRaises call because it's being raised on the previous line. If you look carefully at the traceback, you'll see that the checkListItem was called by the FilterList class's __init__ method, which in turn was called when you try to create self.flObj in your test.
I've just been reading an article that talks about implementing a parser in python:
http://effbot.org/zone/simple-top-down-parsing.htm
The general idea behind the code is described in this paper: http://mauke.hopto.org/stuff/papers/p41-pratt.pdf
Being fairly new to writing parsers in python so I'm trying to write something similar as a learning exercise. However when I attempted to try to code up something similar to what was found in the article I am getting an TypeError: unbound method TypeError. This is the first time I've encountered such an error and I've spent all day trying to figure this out but I haven't solved the issue. Here is a minimal code example (in it's entirety) that has this problem:
import re
class Symbol_base(object):
""" A base class for all symbols"""
id = None # node/token type name
value = None #used by literals
first = second = third = None #used by tree nodes
def nud(self):
""" A default implementation for nud """
raise SyntaxError("Syntax error (%r)." % self.id)
def led(self,left):
""" A default implementation for led """
raise SyntaxError("Unknown operator (%r)." % self.id)
def __repr__(self):
if self.id == "(name)" or self.id == "(literal)":
return "(%s %s)" % (self.id[1:-1], self.value)
out = [self.id, self.first, self.second, self.third]
out = map(str, filter(None,out))
return "(" + " ".join(out) + ")"
symbol_table = {}
def symbol(id, bindingpower=0):
""" If a given symbol is found in the symbol_table return it.
If the symblo cannot be found theni create the appropriate class
and add that to the symbol_table."""
try:
s = symbol_table[id]
except KeyError:
class s(Symbol_base):
pass
s.__name__ = "symbol:" + id #for debugging purposes
s.id = id
s.lbp = bindingpower
symbol_table[id] = s
else:
s.lbp = max(bindingpower,s.lbp)
return s
def infix(id, bp):
""" Helper function for defining the symbols for infix operations """
def infix_led(self, left):
self.first = left
self.second = expression(bp)
return self
symbol(id, bp).led = infix_led
#define all the symbols
infix("+", 10)
symbol("(literal)").nud = lambda self: self #literal values must return the symbol itself
symbol("(end)")
token_pat = re.compile("\s*(?:(\d+)|(.))")
def tokenize(program):
for number, operator in token_pat.findall(program):
if number:
symbol = symbol_table["(literal)"]
s = symbol()
s.value = number
yield s
else:
symbol = symbol_table.get(operator)
if not symbol:
raise SyntaxError("Unknown operator")
yield symbol
symbol = symbol_table["(end)"]
yield symbol()
def expression(rbp = 0):
global token
t = token
token = next()
left = t.nud()
while rbp < token.lbp:
t = token
token = next()
left = t.led(left)
return left
def parse(program):
global token, next
next = tokenize(program).next
token = next()
return expression()
def __main__():
print parse("1 + 2")
if __name__ == "__main__":
__main__()
When I try to run this with pypy:
Traceback (most recent call last):
File "app_main.py", line 72, in run_toplevel
File "parser_code_issue.py", line 93, in <module>
__main__()
File "parser_code_issue.py", line 90, in __main__
print parse("1 + 2")
File "parser_code_issue.py", line 87, in parse
return expression()
File "parser_code_issue.py", line 81, in expression
left = t.led(left)
TypeError: unbound method infix_led() must be called with symbol:+ instance as first argument (got symbol:(literal) instance instead)
I'm guessing this happens because I don't create an instance for the infix operations but I'm not really wanting to create an instance at that point. Is there some way I can change those methods without creating instances?
Any help explaining why this is happening and what I can do to fix the code is greatly appreciated!
Also is this behaviour going to change in python 3?
You forgot to create an instance of the symbol in your tokenize() function; when not a number, yield symbol(), not symbol:
else:
symbol = symbol_table.get(operator)
if not symbol:
raise SyntaxError("Unknown operator")
yield symbol()
With that one change your code prints:
(+ (literal 1) (literal 2))
You haven't bound new function to the instance of your object.
import types
obj = symbol(id, bp)
obj.led = types.MethodType(infix_led, obj)
See accepted answer to another SO question