Python string list printing with escape characters? - python

I have a list variable, with a string in it. For some reason, it's printing like this:
print('Value = ' + str(var))
outputs:
Value = [\'123456789\']
It seems as if those are the escaped quote ticks added by the str function, but I don't know how to get rid of them.
EDIT: Sorry for the confusion. Here is the more complete back-story:
I'm writing a chat analytics app. Here's my Message class:
class Message:
def __init__(self, name, text, likers):
self.name = name
self.text = text
self.likers = likers
def __str__(self):
return 'Message: ' + self.name + ' said "' + self.text + '", liked by ' + str(self.likers)
This is where messages are stored:
message = data['response']['messages'][i]
user_id = message['user_id']
name = message['name'] # Sender
text = re.sub(r'\W+', ' ', str(message['text']))
likers = message['favorited_by']
msg = Message(name, text, likers)
users[user_id].messages.append(msg)
Above, data is a JSON returned from the chat server. i is my variable to loop through the messages (received in blocks of 20). users is a dictionary mapping user ID's to User objects (my other class). Since each message can be liked by multiple users, likers is an array of user ID's.
The problem arises when I'm trying to print messages. Test code:
print([str(x) for x in users[user_names['MyUser']].messages])
(user_names is a dictionary that maps usernames to user ID's)
That code prints:
['Message: MyUser said "all of you", liked by [\'25843278\']', 'Message: MyUser said "why?", liked by [\'25843278\']']

In your code, likers is a list of string.
likers = ['25843278']
If you use str, Python will convert this list into a string, like this:
text = str(likers)
print(repr(text))
# -> "['25843278']"
So, your __str__ method will return a string with single quote.
Further, you create a list of messages:
[str(x) for x in users[user_names['MyUser']].messages]
Python will print this list. It will convert each item to its string representation. So it will escape the single quotes of each likers.
To avoid this, you can convert the list of likers into a string yourself, like this:
def __str__(self):
return ('Message: ' + self.name + ' said "' + self.text + '",
liked by ' + ', '.join(self.likers))

Related

How to Construct Method and Attributes using Dictionaries in Python

class print_values:
def __init__(self,username,user_email,displayname):
self.name= username
self.email=user_email
self.DisplayName=displayname
def printing_content(self):
print(f"UserName: {self.name}\n"
f"UserEmail: {self.email}\n"
f"UserDisplayName:{self.DisplayName}\n")
user_one={'username':'userone',
'useremail':'userone#gmail.com',
'displayname':'User One'}
user_two={'username':'usertwo',
'useremail':'usertwo#gmail.com',
'displayname':'User Two'}
user_three={'username':'userthree',
'useremail':'userthree#gmail.com',
'displayname':'User Three'}
users_list=['user_one','user_two','user_three']
obj_name=print_values(user_one['username'],user_one['useremail'],user_one['displayname'])
obj_name.printing_content()
It's working fine, as am getting output as below
UserName: userone
UserEmail: userone#gmail.com
UserDisplayName:User One
Here am only using user_one dict, i want to do the same for multiple dict.
I have tried adding the dict names in list and try to loop through them, like below
for item in user_list:
obj_name=print_values(item['username'],item['useremail'],item['displayname'])
obj_name.printing_content()
But am getting below error
obj_name=print_values(item['username'],item['useremail'],item['displayname'])
TypeError: string indices must be integers
Any one do let me know what am i missing or anyother idea to get this done.
Thanks in advance!
This is because in users_list=['user_one', 'user_two', 'user_three'] you enter the variable name as a string.
class print_values:
def __init__(self,username,user_email,displayname):
self.name= username
self.email=user_email
self.DisplayName=displayname
def printing_content(self):
print(f"UserName: {self.name}\n"
f"UserEmail: {self.email}\n"
f"UserDisplayName:{self.DisplayName}\n")
user_one={'username':'userone',
'useremail':'userone#gmail.com',
'displayname':'User One'}
user_two={'username':'usertwo',
'useremail':'usertwo#gmail.com',
'displayname':'User Two'}
user_three={'username':'userthree',
'useremail':'userthree#gmail.com',
'displayname':'User Three'}
users_list=[user_one,user_two,user_three] # edited
obj_name=print_values(user_one['username'],user_one['useremail'],user_one['displayname'])
obj_name.printing_content()
for item in users_list:
obj_name=print_values(item['username'],item['useremail'],item['displayname'])
obj_name.printing_content()
Explanation
Your users_list=['user_one', 'user_two', 'user_three'] is a string containing the variable names as the string. When you loop on user_list
for item in user_list:
Here item is not the user_one, or user_two as a variable but these are as the string means 'user_one', or 'user_two', so when you try to get values like item['username'], here you got the error because the item is not a dictionary or json or ..., but it is a string here, you can get the only provide an integer inside these brackets [], like 1, 2, 3, 4,..., ∞.
I hope you understand well. Thanks.
Don't make a dictionary for every user.
Use this code
class Users:
def __init__(self) -> None:
self.userList = []
def addUser(self, user):
self.userList.append(user)
class User:
def __init__(self, username, email, name) -> None:
self.username = username
self.email = email
self.name = name
def __str__(self) -> str:
return f"Username = {self.username}\nEmail = {self.email}\nName = {self.name}\n"
users = Users()
users.addUser(User("username1", "email1", "name1"))
users.addUser(User("username2", "email2", "name2"))
# First way of printing
for user in users.userList:
print(user) # Printing user directly prints the formatted output
# Because I have changed the magic `__str__` method in user class
# You can return anything('string data type only') in __str__ it will print when you print the class object.
# Second way of printing.
for user in users.userList:
print("Username = " + user.username)
print("Email = " + user.email)
print("Name = " + user.name)
print() # for adding one extra line

how to concatenate tow Values as strings into an odoo Char field?

i'm trying to get fields from another model then do some operation on them, there is no problem with logic but I'm getting this error when the methods runs
psycopg2.DataError: invalid input syntax for type double precision: "1.007 t"
these all what I have done
class uom_custom(models.Model):
_inherit = 'product.template'
uom_qty = fields.Char(store=True,compute='get_qty')
#api.depends('qty_available')
def get_qty(self):
uoms=self.env['uom.uom'].search(['&',('category_id', '=', self.uom_id.category_id.id),('show_qty','=',True)])
if uoms.uom_type == 'bigger':
self.uom_qty= str(str(self.qty_available / uoms.factor_inv) + ' ' + uoms.name)
elif self.uom_type =='smaller':
self.uom_qty= str(self.qty_available * uoms.factor_inv) + ' ' + uoms.name
else:
self.uom_qty= str(self.qty_available) + ' ' + uoms.name
return self.uom_qty
so how can I display the value of mathematic operation and uom name beside it
Thanks in advance
The error states that the column in database is defined as double precision. Are you sure you've restarted Odoo and updated your module?
And there are some common mistakes in your compute method. Firstly and i can't repeat it often enough: try to stick to the Odoo naming guideline and name it compute_uom_qty. Secondly without a special decorator a compute method can and will be called with more than one record, so loop on that records. Thirdly: you search for uom.uom which can lead to more than one record, too. So either limit the search to one record or/and implement a check if something was found. uoms.name can lead to SingletonError. And at last: you don't have to return anything in compute methods.
#api.depends('qty_available')
def compute_uom_qty(self):
for record in self:
uoms = self.env['uom.uom'].search(
[('category_id', '=', record.uom_id.category_id.id),
('show_qty','=',True)], limit=1)
if uoms.uom_type == 'bigger':
qty = record.qty_available / uoms.factor_inv
record.uom_qty = "{} {}".format(qty, uoms.name)
elif uoms.uom_type =='smaller':
qty = record.qty_available * uoms.factor_inv
record.uom_qty = "{} {}".format(qty, uoms.name)
else:
record.uom_qty = "{} {}".format(record.qty_available, uoms.name)

Replacing multiple words in a string from different data sets in Python

Essentially I have a python script that loads in a number of files, each file contains a list and these are used to generate strings. For example: "Just been to see $film% in $location%, I'd highly recommend it!" I need to replace the $film% and $location% placeholders with a random element of the array of their respective imported lists.
I'm very new to Python but have picked up most of it quite easily but obviously in Python strings are immutable and so handling this sort of task is different compared to other languages I've used.
Here is the code as it stands, I've tried adding in a while loop but it would still only replace the first instance of a replaceable word and leave the rest.
#!/usr/bin/python
import random
def replaceWord(string):
#Find Variable Type
if "url" in string:
varType = "url"
elif "film" in string:
varType = "film"
elif "food" in string:
varType = "food"
elif "location" in string:
varType = "location"
elif "tvshow" in string:
varType = "tvshow"
#LoadVariableFile
fileToOpen = "/prototype/default_" + varType + "s.txt"
var_file = open(fileToOpen, "r")
var_array = var_file.read().split('\n')
#Get number of possible variables
numberOfVariables = len(var_array)
#ChooseRandomElement
randomElement = random.randrange(0,numberOfVariables)
#ReplaceWord
oldValue = "$" + varType + "%"
newString = string.replace(oldValue, var_array[randomElement], 1)
return newString
testString = "Just been to see $film% in $location%, I'd highly recommend it!"
Test = replaceWord(testString)
This would give the following output: Just been to see Harry Potter in $location%, I'd highly recommend it!
I have tried using while loops, counting the number of words to replace in the string etc. however it still only changes the first word. It also needs to be able to replace multiple instances of the same "variable" type in the same string, so if there are two occurrences of $film% in a string it should replace both with a random element from the loaded file.
The following program may be somewhat closer to what you are trying to accomplish. Please note that documentation has been included to help explain what is going on. The templates are a little different than yours but provide customization options.
#! /usr/bin/env python3
import random
PATH_TEMPLATE = './prototype/default_{}s.txt'
def main():
"""Demonstrate the StringReplacer class with a test sting."""
replacer = StringReplacer(PATH_TEMPLATE)
text = "Just been to see {film} in {location}, I'd highly recommend it!"
result = replacer.process(text)
print(result)
class StringReplacer:
"""StringReplacer(path_template) -> StringReplacer instance"""
def __init__(self, path_template):
"""Initialize the instance attribute of the class."""
self.path_template = path_template
self.cache = {}
def process(self, text):
"""Automatically discover text keys and replace them at random."""
keys = self.load_keys(text)
result = self.replace_keys(text, keys)
return result
def load_keys(self, text):
"""Discover what replacements can be made in a string."""
keys = {}
while True:
try:
text.format(**keys)
except KeyError as error:
key = error.args[0]
self.load_to_cache(key)
keys[key] = ''
else:
return keys
def load_to_cache(self, key):
"""Warm up the cache as needed in preparation for replacements."""
if key not in self.cache:
with open(self.path_template.format(key)) as file:
unique = set(filter(None, map(str.strip, file)))
self.cache[key] = tuple(unique)
def replace_keys(self, text, keys):
"""Build a dictionary of random replacements and run formatting."""
for key in keys:
keys[key] = random.choice(self.cache[key])
new_string = text.format(**keys)
return new_string
if __name__ == '__main__':
main()
The varType you are assigning will be set in only one of your if-elif-else sequence and then the interpreter will go outside. You would have to run all over it and perform operations. One way would be to set flags which part of sentence you want to change. It would go that way:
url_to_change = False
film_to_change = False
if "url" in string:
url_to_change = True
elif "film" in string:
film_to_change = True
if url_to_change:
change_url()
if film_to_change:
change_film()
If you want to change all occurances you could use a foreach loop. Just do something like this in the part you are swapping a word:
for word in sentence:
if word == 'url':
change_word()
Having said this, I'd reccomend introducing two improvements. Push changing into separate functions. It would be easier to manage your code.
For example function for getting items from file to random from could be
def load_variable_file(file_name)
fileToOpen = "/prototype/default_" + file_name + "s.txt"
var_file = open(fileToOpen, "r")
var_array = var_file.read().split('\n')
var_file.clos()
return var_array
Instead of
if "url" in string:
varType = "url"
you could do:
def change_url(sentence):
var_array = load_variable_file(url)
numberOfVariables = len(var_array)
randomElement = random.randrange(0,numberOfVariables)
oldValue = "$" + varType + "%"
return sentence.replace(oldValue, var_array[randomElement], 1)
if "url" in sentence:
setnence = change_url(sentence)
And so on. You could push some part of what I've put into change_url() into a separate function, since it would be used by all such functions (just like loading data from file). I deliberately do not change everything, I hope you get my point. As you see with functions with clear names you can write less code, split it into logical, reusable parts, no needs to comment the code.
A few points about your code:
You can replace the randrange with random.choice as you just
want to select an item from an array.
You can iterate over your types and do the replacement without
specifying a limit (the third parameter), then assign it to the same object, so you keep all your replacements.
readlines() do what you want for open, read from the file as store the lines as an array
Return the new string after go through all the possible replacements
Something like this:
#!/usr/bin/python
import random
def replaceWord(string):
#Find Variable Type
types = ("url", "film", "food", "location", "tvshow")
for t in types:
if "$" + t + "%" in string:
var_array = []
#LoadVariableFile
fileToOpen = "/prototype/default_" + varType + "s.txt"
with open(fname) as f:
var_array = f.readlines()
tag = "$" + t + "%"
while tag in string:
choice = random.choice(var_array)
string = string.replace(tag, choice, 1)
var_array.remove(choice)
return string
testString = "Just been to see $film% in $location%, I'd highly recommend it!"
new = replaceWord(testString)
print(new)

Python: Append to list owned by an instance stored in shelved dictionary

So I'm writing a script to keep track of my correspondence.
It takes the name of someone who emailed me, looks for its associated 'Friend object' in a shelved dictionary of Friend instances (or creates itself a new instance and stores it in the dictionary), then appends the current time to that instance's list of timestamps.
So for example, if the script runs with 'John Citizen' as the input, it finds the key 'John Citizen' in the dictionary, gets the instance associated with that key, goes to that instance's list and appends to that list a timestamp.
This is all working as I want it to, except for the appending.
Here's the friend object:
class Friend():
def __init__(self, name):
self.name = name
self.timestamps = []
def add_timestamp(self, timestamp):
self.timestamps.append(timestamp)
Here's the global function that processes the input into a name string. (The input comes from AppleScript and is always in this format:
Input: "First [Middles] Last <emailaddress#email.com>"
def process_arguments():
parser = argparse.ArgumentParser()
parser.add_argument("sender", help="The output from AppleScript")
args = parser.parse_args()
full_name = args.sender
## gets just the name
words = full_name.split(' ')
words.pop()
full_name = ' '.join(words)
## takes away all the non alpha characters, newlines etc.
alpha_characters = []
for character in full_name:
if character.isalpha() or character == " ":
alpha_characters.append(character)
full_name = ''.join(alpha_characters)
return full_name
And then here's the script handling that full_name string.
## Get the timestamp and name
now = datetime.datetime.now()
full_name = process_arguments()
## open the shelf to store and access all the friend information
shelf = shelve.open('/Users/Perrin/Library/Scripts/friend_shelf.db')
if full_name in shelf:
shelf[full_name].add_timestamp(now)
shelf.close
else:
shelf[full_name] = Friend(full_name)
shelf.close
I've tried to debug it and full_name in shelf evaluates to True and the problem is still happening.
I just can't seem to get that self.timestamps list to populate. I would really appreciate any help!
You need to extract, mutate and store the object back in the shelf to persist it.
e.g
# extract
this_friend = shelf[full_name]
# mutate
this_friend.add_timestamp(now)
# re-add
shelf[full_name] = this_friend
shelf.close()
You can see an example of this in the python docs.
The other option is pass the writeback parameter as True to shelve.open and it will allow you to write to the keys directly.
#paulrooney answered this.
Objects in the shelf need to be removed from the shelf, assigned to a name, mutated (to add time stamp), then put back in the shelf. This code works fine.
shelf = shelve.open('/Users/Perrin/Library/Scripts/friend_shelf.db')
if full_name in shelf:
this_friend = shelf[full_name]
this_friend.add_timestamp(now)
shelf[full_name] = this_friend
print shelf[full_name].timestamps
shelf.close
else:
shelf[full_name] = Friend(full_name)
this_friend = shelf[full_name]
this_friend.add_timestamp(now)
shelf[full_name] = this_friend
shelf.close

How to write tests for generic method used to insert queries

I have a database class and this class contains a method used to insert records. This is how the method looks:
def insertRecord(self, **kwargs):
if 'table' not in kwargs.keys():
raise Exception('The table keyword is required')
table = kwargs['table']
del kwargs['table']
query_fields = kwargs.keys()
pg_fields = []
for field in query_fields:
pg_fields.append('%(' + field + ')s')
query_field_string = ', '.join(query_fields)
query_pg_string = ', '.join(pg_fields)
self.cur.execute('INSERT INTO ' + table + '(' +
query_field_string + ') VALUES (' + query_pg_string + ')',
kwargs
)
self.conn.commit()
The method accepts variable arguments list so the user can use this method to insert entries in any table. Bassically, the method is constructing a query string of the form INSERT INTO <table>(<field1>, <field2>...) VALUES (%(field1)s, %(field2)s...), since the execute method accepts as the second argument a dictionary of the form <field>: <value> all the strings of the form %(field)s will be replaced with the corresponding value.
Basically, the method works fine, but I don't know how should I test it. Should I make a test database and see if the values passed to it are in the database after calling it? How would you write tests for such a method?
Refactor the code to format the SQL command, then test that. In this way it's much simpler -- pass in args, get a formatted string and a dictionary back. No mocking needed.
source
# python -m unittest insertrec
import unittest
def formatInsert(table, **kwargs):
assert table
query_fields = kwargs.keys()
pg_fields = []
for field in query_fields:
pg_fields.append('%(' + field + ')s')
query_field_string = ', '.join(query_fields)
query_pg_string = ', '.join(pg_fields)
return (
'INSERT INTO ' + table + '(' +
query_field_string + ') VALUES (' + query_pg_string + ')',
kwargs
)
class TestInsert(unittest.TestCase):
def test_err_notable(self):
self.assertRaises(AssertionError, formatInsert, None)
def test_insert1(self):
self.assertEquals(
formatInsert('mytab', beer='tasty'),
('INSERT INTO mytab(beer) VALUES (%(beer)s)',
{'beer': 'tasty'}
),
)

Categories

Resources