I am making a database gui program and I am experimenting on how to make the .db file in a different folder from the script. I am using python 3.7. Here is the function that I use:
def get_database_file():
dir_path = os.path.join(os.environ[''], 'test_database')
if not os.path.exists(dir_path):
os.makedirs(dir_path)
file_path = os.path.join(dir_path, 'test_database.db')
if not os.path.exists(file_path):
try:
conn = sqlite3.connect(file_path)
conn.execute("PRAGMA foreign_keys = 1")
except sqlite3.Error as err:
if conn:
conn.rollback() # reverse any changes before the commit
print("SQLite Error: %s" % err.args[0])
sys.exit(-1)
else:
createDatabase(file_path)
print("Finish database created")
finally:
if conn:
conn.close()
else:
return file_path
if os.stat(file_path) == 0:
print("Empty")
else:
print("Not Empty")
return file_path
When I put os.environ['APPDATA'], this function runs fine without any errors. But when I put os.environ['HOME'], it shows this error message:
Traceback (most recent call last):
File "C:/Users/jojot/PycharmProjects/CreateDatabase/gui_database.py", line 4214, in <module>
database_file = get_database_file()
File "C:/Users/jojot/PycharmProjects/CreateDatabase/gui_database.py", line 4178, in get_database_file
dir_path = os.path.join(os.environ['HOME'], 'test_database')
File "C:\Users\jojot\AppData\Local\Programs\Python\Python37-32\lib\os.py", line 678, in __getitem__
raise KeyError(key) from None
KeyError: 'HOME'
I don't understand, what did I do wrong? Please help. Thank you.
It means that you haven't set the environment variable HOME.
If you want the program not to crash if you haven't set that variable you can use:
x = os.environ.get('HOME')
print(x) # None
instead of:
x = os.environ['HOME']
# raise KeyError(key) from None
# KeyError: 'HOME'
Related
code error
Traceback (most recent call last):
File "main.py", line 135, in <module>
run_bot(auth['bot_token'], vk_session)
File "main.py", line 112, in run_bot
bot.polling()
File "/usr/local/lib/python3.8/dist-packages/telebot/__init__.py", line 514, i n polling
self.__threaded_polling(none_stop, interval, timeout, long_polling_timeout)
File "/usr/local/lib/python3.8/dist-packages/telebot/__init__.py", line 573, i n __threaded_polling
raise e
File "/usr/local/lib/python3.8/dist-packages/telebot/__init__.py", line 536, i n __threaded_polling
self.worker_pool.raise_exceptions()
File "/usr/local/lib/python3.8/dist-packages/telebot/util.py", line 117, in ra ise_exceptions
raise self.exception_info
File "/usr/local/lib/python3.8/dist-packages/telebot/util.py", line 69, in run
task(*args, **kwargs)
File "main.py", line 37, in send_text
if users_data[message.chat.id]['mode'] == 'search':
KeyError: 1413951044
code
import itertools
import requests
import configparser
import telebot
import vk_api
from vk_api.audio import VkAudio
import logging
def run_bot(token, vk_session):
users_data = {}
bot = telebot.TeleBot(token)
vk_audio = VkAudio(vk_session)
#bot.message_handler(commands=['search', 'get'])
def start_message(message):
users_data[message.chat.id] = {}
command = message.text.split()[0][1:]
if command == 'search':
users_data[message.chat.id]['mode'] = 'search'
bot.send_message(message.chat.id, "how to search?")
elif command == 'get':
users_data[message.chat.id]['mode'] = 'get'
try:
users_data[message.chat.id]['user_id'] = message.text.split()[1]
users_data[message.chat.id]['page'] = 0
get(message)
except:
bot.send_message(message.chat.id, "error id (how - /get id)")
#bot.message_handler(content_types=['text'])
def send_text(message):
if users_data[message.chat.id]['mode'] == 'search':
search(message)
#bot.callback_query_handler(func=lambda call: True)
def callback_worker(call):
if users_data[call.message.chat.id]['mode'] == 'search':
download(call)
elif users_data[call.message.chat.id]['mode'] == 'get':
if call.data == 'back':
users_data[call.message.chat.id]['page'] -= 5
get(call.message, call.message.message_id)
elif call.data == 'forward':
users_data[call.message.chat.id]['page'] += 5
get(call.message, call.message.message_id)
else:
download(call)
def download(call):
data = users_data[call.message.chat.id][call.data]
try:
print("loading")
bot.send_message(call.message.chat.id, "loading")
open('tmp.mp3', 'wb').write(requests.get(data['url'], allow_redirects=True, verify=False).content)
audio = open('tmp.mp3', "rb")
bot.send_message(call.message.chat.id, "sending")
print("sending")
bot.send_audio(call.message.chat.id, audio, performer=data['artist'], title=data['title'])
except Exception as exc:
print("error")
bot.send_message(call.message.chat.id, exc)
def get_audio_name(audio):
m, s = divmod(audio["duration"], 60)
track_name = "{} - {} ({}:{:0<2})".format(audio["artist"], audio["title"], m, s)
return track_name
def search(message):
print("Message received:", message.text)
items = [it for it in vk_audio.search(message.text, 5)]
if len(items) != 0:
print("On request found", len(items))
keyboard = telebot.types.InlineKeyboardMarkup()
for item in items:
users_data[message.chat.id][str(items.index(item))] = {'artist': item["artist"], 'title': item["title"],
'url': item["url"]}
key = telebot.types.InlineKeyboardButton(text=get_audio_name(item),
callback_data=str(items.index(item)))
keyboard.add(key)
bot.send_message(message.chat.id, text="What are we going to download?", reply_markup=keyboard)
else:
print("Nothing was found on the request")
bot.send_message(message.chat.id, "Nothing was found on the request")
print("Конец")
def get(message, id_msg_to_edit=None):
try:
it = vk_audio.get_iter(users_data[message.chat.id]['user_id'])
itt = itertools.islice(it, users_data[message.chat.id]['page'], users_data[message.chat.id]['page'] + 5)
keyboard = telebot.types.InlineKeyboardMarkup()
costil = 0
for i in itt:
users_data[message.chat.id][str(costil)] = i
keyboard.add(telebot.types.InlineKeyboardButton(text=get_audio_name(i), callback_data=str(costil)))
costil += 1
if users_data[message.chat.id]['page'] != 0:
keyboard.add(telebot.types.InlineKeyboardButton(text="Back", callback_data="back"))
if costil != 4:
keyboard.add(telebot.types.InlineKeyboardButton(text="Forward", callback_data="forward"))
if id_msg_to_edit is None:
bot.send_message(message.chat.id, text='What will we download?', reply_markup=keyboard)
else:
bot.edit_message_reply_markup(message.chat.id, id_msg_to_edit, reply_markup=keyboard)
except Exception as exc:
bot.send_message(message.chat.id, text=exc)
bot.polling()
What could be the problem here?
message.chat.id at line 37 is resolved to value 1413951044 and this key is not present in users_data dictionary, hence KeyError is raised.
If you just want to get rid of this error, enclose line 37 in a try/except block like below:
try:
if users_data[message.chat.id]['mode'] == 'search':
except KeyError:
pass
I'm making a program that gets info from a website about games, among that info, images, since i'm trying to download info of all games on that website, using a single thread with a 1Mbps connection would be very painful, so i decided to take action against this issue and programmed to spawn a thread for each letter of the alphabet that a game starts with, (games can be filtered by such). So, inside the function that downloads the corresponding image to certain game, while i have more than one thread, at some point in execution (sooner or later) an error is raised, then inside the except block that handles it, another exception is raised, and so on, over and over... this immediately causes threads to come to an end, but the fact is that, when i'm left with only a petty single thread to rely on, that thread goes on very well without giving any trouble.
Question:
How to solve this, and why is it happening?
Deduction:
I think that, when multiple threads get to requests.get line inside the download_image function (the function where the very problem must lie), maybe it fails because of multiple requests... that is as far as i can try to guess.
I really don't have the least idea of how to solve this, that being said, i would appreciate any help, thanks in advance.
I got rid of all the functions not having to do anything with the problem described above.
I spawn the threads at program's end, and each thread target function is named get_all_games_from_letter.
CODE
from bs4 import BeautifulSoup
from string import ascii_lowercase
from datetime import date
from vandal_constants import *
from PIL import Image
from requests.exceptions import ConnectionError
from exceptions import NoTitleException
from validator_collection import url as url_check
from rawgpy import RAWG
from io import BytesIO
import traceback
import requests
import threading
import sqlite3
import concurrent.futures
### GLOBALS #####
FROM_RAWG = False
INSERT_SQL = ''
# CONSTANTS ########
rawg = RAWG('A Collector')
#################
def download_image(tag=None, game=None, rawg_game=None):
if tag:
return sqlite3.Binary(requests.get(url).content) if (url := tag['data-src']) else None
elif game:
global FROM_RAWG
img_tag = game.select_one(IMG_TAG_SELECTOR)
if img_tag and img_tag.get('data-src', None):
try:
if url_check(img_tag['data-src']):
return sqlite3.Binary(requests.get(img_tag['data-src']).content)
print(f"{img_tag['data-src']} is NOT a valid url")
except ConnectionError:
try:
print('Error While downloading from "Vandal.elespannol.com" website:')
traceback.print_exc()
except Exception:
print('Another Exception Ocurred')
traceback.print_exc()
except OSError:
print('Error en el Handshake parece')
traceback.print_exc()
FROM_RAWG = True
if rawg_game and getattr(rawg_game, 'background_image', None):
try:
print('Continue to download from RAWG')
return sqlite3.Binary(requests.get(rawg_game.background_image).content)
except ConnectionError:
print('Error While downloading from RAWG:')
traceback.print_exc()
return None
def prepare_game_record(game, db_games_set):
global INSERT_SQL
title = getattr(game.select_one(TITLE_TAG_SELECTOR), 'text', None)
if not title:
raise NoTitleException()
if title in db_games_set:
print(f'Already Have {title} in database')
return None
description = game.select_one(DESCRIPTION_TAG_SELECTOR)
rawg_game = None
try:
rawg_game = rawg.search(title)[0]
except Exception as err:
print('No rawg')
traceback.print_exc()
game_data = {
'nombre': title,
'descripcion': description.text if description else rawg_game.description if rawg_game else '',
'genero': genres if (genres := translate_genres(game.select_one(GENRES_TAG_SELECTOR).contents[1].strip().split(' / '))) else '',
'fondo': resize_image(img) if (img := download_image(game=game, rawg_game=rawg_game)) and not FROM_RAWG else img,
'year': None,
}
if not INSERT_SQL:
INSERT_SQL = construct_sql_insert(**game_data)
if hasattr(rawg_game, 'released'):
game_data['year'] = date.fromisoformat(rawg_game.released).year
return tuple(game_data.values())
def get_all_games_from_letter(letter):
global FROM_RAWG
counter = 36
hashes_set = set()
with sqlite3.connect('/media/l0new0lf/LocalStorage/data.db') as connection:
cursor = connection.cursor()
cursor.execute(f'SELECT nombre FROM juegos where nombre like "{letter.upper()}%"')
db_games_set = []
for row in cursor:
db_games_set.append(row[0])
db_games_set = set(db_games_set)
while True:
try:
prepared_games = []
rq = requests.get(
f'https://vandal.elespanol.com/juegos/13/pc/letra/{letter}/inicio/{counter}')
if rq:
print('Request GET: from ' +
f'https://vandal.elespanol.com/juegos/13/pc/letra/{letter}/inicio/{counter}' + ' Got Workable HTML !')
else:
print('Request GET: from ' +
f'https://vandal.elespanol.com/juegos/13/pc/letra/{letter}/inicio/{counter}' + ' Not Working !!, getting next page!')
continue
if rq.status_code == 301 or rq.status_code == 302 or rq.status_code == 303 or rq.status_code == 304:
print(f'No more games in letter {letter}\n**REDIRECTING TO **')
break
counter += 1
soup = BeautifulSoup(rq.content, 'lxml')
main_table = soup.select_one(GAME_SEARCH_RESULTS_TABLE_SELECTOR)
if hash(main_table.get_text()) not in hashes_set:
hashes_set.add(hash(main_table.get_text()))
else:
print('Repeated page ! I\'m done with this letter.')
break
game_tables = main_table.find_all(
'table', {'class': GAME_TABLES_CLASS})
print('entering game_tables loop')
for game in game_tables:
FROM_RAWG = False
try:
game_record = prepare_game_record(game, db_games_set)
except NoTitleException:
print('There is no title for this game, DISCARDING!')
continue
except Exception as err:
print('Unknown ERROR in prepare_games_record function')
traceback.print_exc()
continue
if not game_record:
continue
prepared_games.append(game_record)
print('Game successfully prepared !')
if prepared_games:
print(f'Thread, Writing to Database')
try:
cursor.executemany(INSERT_SQL, prepared_games)
connection.commit()
except Exception as err:
print(err)
print('done')
except Exception as err:
print('TRULY UNEXPECTED EXCEPTION')
print(err)
traceback.print_exc()
continue
#get_all_games_from_letter('c') You use a single thread?, no trouble at all!!
with concurrent.futures.ThreadPoolExecutor(len(ascii_lowercase)) as executor:
for letter in ascii_lowercase:
executor.submit(get_all_games_from_letter, letter)
Error Stack Trace:
Note: This is only part of the errors, but the rest is the very same.
Game successfully prepared !
Error While downloading from "Vandal.elespannol.com" website:
Traceback (most recent call last):
File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 665, in urlopen
httplib_response = self._make_request(
File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 376, in _make_request
self._validate_conn(conn)
File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 996, in _validate_conn
conn.connect()
File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 366, in connect
self.sock = ssl_wrap_socket(
File "/usr/lib/python3/dist-packages/urllib3/util/ssl_.py", line 370, in ssl_wrap_socket
return context.wrap_socket(sock, server_hostname=server_hostname)
File "/usr/lib/python3.8/ssl.py", line 500, in wrap_socket
return self.sslsocket_class._create(
File "/usr/lib/python3.8/ssl.py", line 1040, in _create
self.do_handshake()
File "/usr/lib/python3.8/ssl.py", line 1309, in do_handshake
self._sslobj.do_handshake()
OSError: [Errno 0] Error
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/lib/python3/dist-packages/requests/adapters.py", line 439, in send
resp = conn.urlopen(
File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 719, in urlopen
retries = retries.increment(
File "/usr/lib/python3/dist-packages/urllib3/util/retry.py", line 400, in increment
raise six.reraise(type(error), error, _stacktrace)
File "/usr/lib/python3/dist-packages/six.py", line 702, in reraise
raise value.with_traceback(tb)
File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 665, in urlopen
httplib_response = self._make_request(
File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 376, in _make_request
self._validate_conn(conn)
File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 996, in _validate_conn
conn.connect()
File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 366, in connect
self.sock = ssl_wrap_socket(
File "/usr/lib/python3/dist-packages/urllib3/util/ssl_.py", line 370, in ssl_wrap_socket
return context.wrap_socket(sock, server_hostname=server_hostname)
File "/usr/lib/python3.8/ssl.py", line 500, in wrap_socket
return self.sslsocket_class._create(
File "/usr/lib/python3.8/ssl.py", line 1040, in _create
self.do_handshake()
File "/usr/lib/python3.8/ssl.py", line 1309, in do_handshake
self._sslobj.do_handshake()
urllib3.exceptions.ProtocolError: ('Connection aborted.', OSError(0, 'Error'))
To solve the problem what one would need is just add a global lock in order that when each thread tries to request.get an image, it has to ask in the first place if some thread is already using it. That is, downloading an image is restricted to just simultaneously one use for all threads
#######GLOBALS####
lock = threading.Lock() #Add this to globals variables
##################
def download_image(tag=None, game=None, rawg_game=None):
if tag:
return sqlite3.Binary(requests.get(url).content) if (url := tag['data-src']) else None
elif game:
global FROM_RAWG
img_tag = game.select_one(IMG_TAG_SELECTOR)
if img_tag and img_tag.get('data-src', None):
try:
if url_check(img_tag['data-src']):
lock.acquire() #acquire the lock for downloading (it means other threads must wait until the one that acquired finishes)
temp = sqlite3.Binary(requests.get(img_tag['data-src']).content)
lock.release() # release the lock when done with receiving the HttpResponse
return temp
print(f"{img_tag['data-src']} is NOT a valid url")
except ConnectionError:
try:
print('Error While downloading from "Vandal.elespannol.com" website:')
traceback.print_exc()
except Exception:
print('Another Exception Ocurred')
traceback.print_exc()
except OSError:
print('Error en el Handshake parece')
traceback.print_exc()
FROM_RAWG = True
if rawg_game and getattr(rawg_game, 'background_image', None):
try:
print('Continue to download from RAWG')
lock.acquire() #acquire the lock for downloading (it means other threads must wait until the one that acquired finishes)
temp = sqlite3.Binary(requests.get(rawg_game.background_image).content)
lock.release() # release the lock when done with
return temp
except ConnectionError:
print('Error While downloading from RAWG:')
traceback.print_exc()
return None
And done, no more troubles with downloading images in multiple threads.... but still... i don't actually know why i would need to make sure of that one request.get is made for all threads, i thought OS handles this issue by using queues or something.
I have inspected this SO question on how to gracefully close out the asyncio process. Although, when I perform it on my code:
async def ob_main(product_id: str, freq: int) -> None:
assert freq >= 1, f'The minimum frequency is 1s. Adjust your value: {freq}.'
save_loc = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'ob', product_id)
while True:
close = False
try:
full_save_path = create_save_location(save_loc)
file = open(full_save_path, 'a', encoding='utf-8')
await ob_collector(product_id, file)
await asyncio.sleep(freq)
except KeyboardInterrupt:
close = True
task.cancel()
loop.run_forever()
task.exception()
except:
exc_type, exc_value, exc_traceback = sys.exc_info()
error_msg = repr(traceback.format_exception(exc_type, exc_value, exc_traceback))
print(error_msg)
logger.warning(f'[1]-Error encountered collecting ob data: {error_msg}')
finally:
if close:
loop.close()
cwow()
exit(0)
I get the following traceback printed in terminal:
^C['Traceback (most recent call last):\n', ' File "/anaconda3/lib/python3.7/asyncio/runners.py", line 43, in run\n return loop.run_until_complete(main)\n', ' File "/anaconda3/lib/python3.7/asyncio/base_events.py", line 555, in run_until_complete\n self.run_forever()\n', ' File "/anaconda3/lib/python3.7/asyncio/base_events.py", line 523, in run_forever\n self._run_once()\n', ' File "/anaconda3/lib/python3.7/asyncio/base_events.py", line 1722, in _run_once\n event_list = self._selector.select(timeout)\n', ' File "/anaconda3/lib/python3.7/selectors.py", line 558, in select\n kev_list = self._selector.control(None, max_ev, timeout)\n', 'KeyboardInterrupt\n', '\nDuring handling of the above exception, another exception occurred:\n\n', 'Traceback (most recent call last):\n', ' File "coinbase-collector.py", line 98, in ob_main\n await asyncio.sleep(freq)\n', ' File "/anaconda3/lib/python3.7/asyncio/tasks.py", line 564, in sleep\n return await future\n', 'concurrent.futures._base.CancelledError\n']
and the code keeps running.
task and loop are the variables from the global scope, defined in the __main__:
loop = asyncio.get_event_loop()
task = asyncio.run(ob_main(args.p, 10))
Applying this question's method solves the issue. So in the above case:
try:
loop.run_until_complete(ob_main(args.p, 10))
except KeyboardInterrupt:
cwow()
exit(0)
However, I do not uderstand why that works.
I am making a python script that initially created threads and used them to brute force port 22 on my local machine using a wordlist, part of an infosec project.
I had issues when there were too many threads and I wanted to kill them off elegantly and exit the program and so I started to look at multiprocessing instead based this post's answer by by user cfi.
The problem I have is that when I run the program I am getting the below error.
python3 ssh_brute.py
[*] Pass not found
Traceback (most recent call last):
File "/Users/richardcurteis/anaconda3/lib/python3.7/multiprocessing/queues.py", line 236, in _feed
obj = _ForkingPickler.dumps(obj)
File "/Users/richardcurteis/anaconda3/lib/python3.7/multiprocessing/reduction.py", line 51, in dumps
cls(buf, protocol).dump(obj)
File "/Users/richardcurteis/anaconda3/lib/python3.7/multiprocessing/process.py", line 330, in __reduce__
'Pickling an AuthenticationString object is '
TypeError: Pickling an AuthenticationString object is disallowed for security reasons
I assume I am doing something wrong with the multiprocessing API but I am not sure what exactly. I have looked at the docs and I believe I am more or less on track.
What am I missing?
Code:
import paramiko
from multiprocessing import Queue, Process
TARGET_IP = 'localhost'
USERNAME = 'richardcurteis'
WORDLIST = 'test2.txt'
MAX_THREADS = 10
processes = []
found = []
q = Queue()
def ssh_connect(target_ip, username, password):
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy)
try:
ssh.connect(target_ip, username=username, password=password)
found.append(password)
q.put(password)
except paramiko.ssh_exception.AuthenticationException:
print("[*] Failed: ", password)
return False
finally:
ssh.close()
return True
def close_threads(abort=False):
for p in processes:
p.join()
if abort:
for x in processes:
x.terminate()
processes.clear()
def main():
with open(WORDLIST) as input_handle:
process_count = 0
for line in input_handle:
try:
password = line.rstrip()
p = Process(target=ssh_connect, args=[TARGET_IP, USERNAME, password])
processes.append(p)
p.start()
q.put(p)
process_count += 1
if not q.empty():
break
if process_count >= MAX_THREADS:
close_threads()
process_count = 0
except KeyboardInterrupt:
print("[!] Interrupted by user")
break
except (ConnectionResetError, paramiko.ssh_exception.SSHException):
print("[X] Connection reset by target. Reduce thread count")
break
close_threads()
if len(found) > 0:
for c in found:
print("[!] Found: ", c)
else:
print("[*] Pass not found")
if __name__ == '__main__':
main()
I have following code which creates a testrail client and executes testrail's GET_SUITES API call.
I have a function to call the GET_SUITES API and I am passing testrail client & test_rail_project_id as params
I am trying to use multiprocessing to execute over my list of projects to speed up things and I am can't pickle error
My code:
from itertools import product
def get_suites(client, project_id):
try:
path = 'get_suites/{projectid}'.format(projectid=project_id)
test_rail_response = client.send_get(path)
return test_rail_response
except Exception as e:
raise Exception(str(e))
if __name__ == "__main__":
testRailClient = APIClient(TESTRAIL_URL)
pool = Pool(2)
all_project_ids = [100, 200, 300]
data = pool.starmap(get_suites, product([testRailClient], all_project_ids))
Error stack:
Traceback (most recent call last):
File "main.py", line 57, in <module>
data = pool.starmap(testrailapi.get_suites, product([testRailClient], all_project_ids))
File "/usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/lib/python3.6/multiprocessing/pool.py", line 274, in starmap
return self._map_async(func, iterable, starmapstar, chunksize).get()
File "/usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/lib/python3.6/multiprocessing/pool.py", line 644, in get
raise self._value
File "/usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/lib/python3.6/multiprocessing/pool.py", line 424, in _handle_tasks
put(task)
File "/usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/lib/python3.6/multiprocessing/connection.py", line 206, in send
self._send_bytes(_ForkingPickler.dumps(obj))
File "/usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/lib/python3.6/multiprocessing/reduction.py", line 51, in dumps
cls(buf, protocol).dump(obj)
TypeError: can't pickle SSLContext objects
Any suggestions please?
Thank you
PS: I am using Python3.6
UPDATE:
As suggested I tried removing the API client as a parameter and it worked but I am getting the same error when I have "get_suites" as a method. Please see my updated code below
class TestRailExecution:
def __init__(self, url, username, password):
self.url = url
self.username = username
self.password = password
self.client = APIClient(self.url)
self.client.user = username
self.client.password = password
def get_suites(self, project_id):
try:
path = 'get_suites/{projectid}'.format(projectid=project_id)
test_rail_response = self.client.send_get(path)
return test_rail_response
except Exception as e:
raise Exception(str(e))
if __name__ == "__main__":
testRailClient = TestRailExecution(TESTRAIL_URL, user, password)
pool = Pool(2)
data = pool.map(get_suites, [100, 200, 300])