When using click I know how to define a multiple choice option. I also know how to set an option as a required one. But, how can I indicate that an option B is required only if the value of option A is foo?
Here's an example:
import click
#click.command()
#click.option('--output',
type=click.Choice(['stdout', 'file']), default='stdout')
#click.option('--filename', type=click.STRING)
def main(output, filename):
print("output: " + output)
if output == 'file':
if filename is None:
print("filename must be provided!")
else:
print("filename: " + str(filename))
if __name__ == "__main__":
main()
If the output option is stdout, then filename is not needed. However, if the user chooses output to be file, then the other option filename must be provided. Is this pattern supported by click?
At the beginning of the function I can add something like:
if output == 'file' and filename is None:
raise ValueError('When output is "file", a filename must be provided')
But I am interested whether there's a nicer/cleaner solution.
In the particular case of this example, I think an easier method would be to get rid of --output, and simply assume stdout if --filename is not specified and if --filename is specified, then use it instead of stdout.
But assuming this is a contrived example, you can inherit from click.Option to allow hooking into the click processing:
Custom Class:
class OptionRequiredIf(click.Option):
def full_process_value(self, ctx, value):
value = super(OptionRequiredIf, self).full_process_value(ctx, value)
if value is None and ctx.params['output'] == 'file':
msg = 'Required if --output=file'
raise click.MissingParameter(ctx=ctx, param=self, message=msg)
return value
Using Custom Class:
To use the custom class, pass it as the cls argument to the option decorator like:
#click.option('--filename', type=click.STRING, cls=OptionRequiredIf)
Test Code:
import click
#click.command()
#click.option('--output',
type=click.Choice(['stdout', 'file']), default='stdout')
#click.option('--filename', type=click.STRING, cls=OptionRequiredIf)
def main(output, filename):
print("output: " + output)
if output == 'file':
if filename is None:
print("filename must be provided!")
else:
print("filename: " + str(filename))
main('--output=file'.split())
Results:
Usage: test.py [OPTIONS]
Error: Missing option "--filename". Required if --output=file
You can do the same thing with a custom validation callback:
import click
def required_with_output(ctx, param, value):
if ctx.params.get("output") != "stdout" and value is None:
raise click.BadParameter("--output requires --filename")
return value
#click.command()
#click.option(
"--output",
type=click.Choice(["stdout", "file"]),
default="stdout",
)
#click.option("--filename", callback=required_with_output)
def main(output, filename):
print("output: " + output)
if output == "file":
if filename is None:
print("filename must be provided!")
else:
print("filename: " + str(filename))
if __name__ == "__main__":
main()
I think this is a little simpler.
I extended the answer by Stephen, and made it more generic:
class OptionRequiredIf(click.Option):
"""
Option is required if the context has `option` set to `value`
"""
def __init__(self, *a, **k):
try:
option = k.pop('option')
value = k.pop('value')
except KeyError:
raise(KeyError("OptionRequiredIf needs the option and value "
"keywords arguments"))
click.Option.__init__(self, *a, **k)
self._option = option
self._value = value
def process_value(self, ctx, value):
value = super(OptionRequiredIf, self).process_value(ctx, value)
if value is None and ctx.params[self._option] == self._value:
msg = 'Required if --{}={}'.format(self._option, self._value)
raise click.MissingParameter(ctx=ctx, param=self, message=msg)
return value
Usage example:
#click.option('--email', type=click.STRING,
help='Settings for sending emails.',
option='output', value='email', cls=OptionRequiredIf)
I was inspired by this answer
Related
I have already written a simple python cmd program using the cmd module. However, I would like it to be modular. For Example, there would be a folder named script and any .py that contains a command would add the command to the application. How would I go about that?
NOTE:
I have figured out how to find and load modules that are within a folder using importlib.
First, you must understand how the cmd module works. I would not go into it here but the gist is command is entered, then split up into the actual command itself and its arguments. The command is then searched in the implementation of the cmd module using getattr(). The resulting function returned is then executed. Raises an error if the function(attribute) is not found.
Add the list of modules added to a list.
lst.append(importlib.importlib.import_module(<module path here>))
When cmd is finding a command, modify the code to get it to run through the list of imported modules and see if the function/command exists in that module. If so, execute it.
rough code to find func
def findfunc(funcname):
for module in lst:
if hasattr(<module class where func is stored>, "<funcname>"):
return getattr(<module class where func is stored>,"<funcname>")
However, I will leave my implementation here. It also includes the capability to reload modules(limited to a certain extent) and private commands that do not show up in help or autocomplete(I am using py prompt toolkit). A fair bit of warning, no docs or help in understanding this code as it is only meant for my personal use only. Meaning don't directly copy it and also It was a modification to the python cmd module.
import importlib
import os
import string
import sys
import time
from prompt_toolkit import PromptSession
from prompt_toolkit.completion import WordCompleter
from prompt_toolkit.styles import Style
from internal.logger import logger # my logger Implementation, ignore it
__all__ = ["cli2"]
PROMPT = '>>> '
IDENTCHARS = string.ascii_letters + string.digits + '_'
style = Style.from_dict(
{
"completion-menu.completion": "bg:#008888 #ffffff",
"completion-menu.completion.current": "bg:#00aaaa #000000",
"scrollbar.background": "bg:#88aaaa",
"scrollbar.button": "bg:#222222",
}
)
def parse(arg):
return arg.split()
class Interpreter:
identchars = IDENTCHARS
ruler = '='
lastcmd = ''
doc_leader = ""
doc_header = "Available Commands (type help <topic> to get documentation for <topic> ):"
misc_header = "Miscellaneous help topics:"
nohelp = "*** No help on %s"
use_rawinput = 1
modulelst = []
internalcmdlst = ["help", "exit", "reload", "listmodules", "listvar"]
def __init__(self, intro=None, prompt=PROMPT, stdin=None, stdout=None):
"""Instantiate a line-oriented interpreter framework.
The optional arguments stdin and stdout
specify alternate input and output file objects; if not specified,
sys.stdin and sys.stdout are used.
"""
self.prompt = prompt
self.intro = intro
if stdin is not None:
self.stdin = stdin
else:
self.stdin = sys.stdin
if stdout is not None:
self.stdout = stdout
else:
self.stdout = sys.stdout
self.cmdqueue = []
def cmdloop(self):
self.preloop()
try:
if self.intro:
self.stdout.write(str(self.intro) + "\n")
stop = None
while not stop:
if self.cmdqueue:
line = self.cmdqueue.pop(0)
else:
line = self.session.prompt(self.prompt)
line = self.precmd(line)
stop = self.onecmd(line)
stop = self.postcmd(stop, line)
self.postloop()
finally:
pass
def precmd(self, line):
"""Hook method executed just before the command line is
interpreted, but after the input prompt is generated and issued.
"""
# Parse command to argument
return self.parseline(line)
def postcmd(self, stop, line):
"""Hook method executed just after a command dispatch is finished."""
return stop
def preloop(self):
"""Hook method executed once when the cmdloop() method is called."""
self.autocommands = [item[3:] for item in self.get_names() if (item[-5:] != '__pvt') and (item[:3] == 'do_')]
self.session = PromptSession(completer=WordCompleter(self.autocommands, ignore_case=False))
def postloop(self):
"""Hook method executed once when the cmdloop() method is about to
return.
"""
self.autocommands, self.session = None, None
def parseline(self, line):
"""Parse the line into a command name and a string containing
the arguments. Returns a tuple containing (command, args, line).
'command' and 'args' may be None if the line couldn't be parsed.
"""
line = line.strip()
if not line:
return None, None, line
elif line[0] == '?':
line = 'help ' + line[1:]
elif line[0] == '!':
if hasattr(self, 'do_shell'):
line = 'shell ' + line[1:]
else:
return None, None, line
i, n = 0, len(line)
while i < n and line[i] in self.identchars: i = i + 1
cmd, arg = line[:i], line[i:].strip()
return cmd, arg, line
def onecmd(self, linecommand):
"""Interpret the argument as though it had been typed in response
to the prompt.
This may be overridden, but should not normally need to be;
see the precmd() and postcmd() methods for useful execution hooks.
The return value is a flag indicating whether interpretation of
commands by the interpreter should stop.
"""
cmd, arg, line = linecommand
if not line:
return self.emptyline()
if cmd is None:
return self.default(line)
self.lastcmd = line
if line == 'EOF':
self.lastcmd = ''
if cmd == '':
return self.default(line)
if cmd in self.internalcmdlst:
return self.getfunc(cmd)(arg)
else:
try:
terminalclass, func = self.getfunc(cmd)
except AttributeError:
return self.default(line)
return func(terminalclass, arg)
def emptyline(self):
"""Called when an empty line is entered in response to the prompt.
If this method is not overridden, it repeats the last nonempty
command entered.
"""
if self.lastcmd:
return self.onecmd(self.lastcmd)
def default(self, line):
"""Called on an input line when the command prefix is not recognized.
If this method is not overridden, it prints an error message and
returns.
"""
self.stdout.write('*** Unknown syntax: %s\n' % line.split(" ")[0])
def get_names(self):
# This method used to pull in base class attributes
# at a time dir() didn't do it yet.
lst = [*dir(self.__class__)]
for modulejs in self.modulelst:
terminalclass = getattr(getattr(modulejs["module"], 'cli_helper'), "Terminal")
lst += dir(terminalclass)
return lst
def print_topics(self, header, cmds, maxcol):
if cmds:
self.stdout.write("%s\n" % str(header))
if self.ruler:
self.stdout.write("%s\n" % str(self.ruler * len(header)))
self.columnize(cmds, maxcol - 1)
self.stdout.write("\n")
def columnize(self, lst, displaywidth=80):
"""Display a list of strings as a compact set of columns.
Each column is only as wide as necessary.
Columns are separated by two spaces (one was not legible enough).
"""
if not lst:
self.stdout.write("<empty>\n")
return
nonstrings = [i for i in range(len(lst))
if not isinstance(lst[i], str)]
if nonstrings:
raise TypeError("list[i] not a string for i in %s"
% ", ".join(map(str, nonstrings)))
size = len(lst)
if size == 1:
self.stdout.write('%s\n' % str(lst[0]))
return
# Try every row count from 1 upwards
for nrows in range(1, len(lst)):
ncols = (size + nrows - 1) // nrows
colwidths = []
totwidth = -2
for col in range(ncols):
colwidth = 0
for row in range(nrows):
i = row + nrows * col
if i >= size:
break
x = lst[i]
colwidth = max(colwidth, len(x))
colwidths.append(colwidth)
totwidth += colwidth + 2
if totwidth > displaywidth:
break
if totwidth <= displaywidth:
break
else:
nrows = len(lst)
ncols = 1
colwidths = [0]
for row in range(nrows):
texts = []
for col in range(ncols):
i = row + nrows * col
if i >= size:
x = ""
else:
x = lst[i]
texts.append(x)
while texts and not texts[-1]:
del texts[-1]
for col in range(len(texts)):
texts[col] = texts[col].ljust(colwidths[col])
self.stdout.write("%s\n" % str(" ".join(texts)))
def addmodulesfromdirectory(self, directory):
try:
directory_contents = os.listdir(directory)
for item in directory_contents:
self.addmodule(os.path.join(directory, item), "".join([directory[2:], ".", item]))
except OSError as e:
print("Something Happened\nPlease try again :)")
logger.error("OSErrot, {}".format(e))
except ImportError as e:
print("Something Went Wrong while importing modules\nPlease try again :)")
logger.error("ImportError, {}".format(e))
except Exception as e:
print("Oops, my fault\nPlease try again :)")
logger.error("Exception, {}".format(e))
finally:
logger.info("Done importing cli modules")
def addmodule(self, pathdir, importdir):
if os.path.isdir(pathdir):
importedmodule = importlib.import_module(importdir)
modulejs = {
"module": importedmodule,
"name": importedmodule.__name__,
"importdir": importdir,
"pathdir": pathdir
}
self.modulelst.append(modulejs)
def removemodule(self, modulename):
if modulename in [x["name"] for x in self.modulelst]:
self.modulelst.remove(self.getmodulejs(modulename))
del sys.modules[modulename]
else:
raise ModuleNotFoundError
def replacemodule(self, modulename):
module = self.getmodulejs(modulename)
self.removemodule(modulename)
self.addmodule(module["pathdir"], module["importdir"])
def getmodulejs(self, modulename):
for i in self.modulelst:
if i['name'] == modulename:
return i
#staticmethod
def getmoduledep(modulename):
unsortedscriptsysmodules = [module for module in sys.modules if (("script" in module) and ("script" != module))]
sortedlst = []
for scriptmodules in unsortedscriptsysmodules:
mod = sys.modules[scriptmodules]
if not (hasattr(mod, "__path__") and getattr(mod, '__file__', None) is None) and (
scriptmodules != "script.{}".format(modulename)):
sortedlst.append(sys.modules[scriptmodules])
return sortedlst
def reloadmoduledep(self, modulename):
for dep in self.getmoduledep(modulename):
try:
importlib.reload(dep)
except ModuleNotFoundError as e:
print(e)
def getfunc(self, command):
if "do_" in command:
command = command[3:]
if command in self.internalcmdlst:
return getattr(self, "_".join(['do', command]))
else:
for modulejs in self.modulelst:
terminalclass = getattr(getattr(modulejs["module"], 'cli_helper'), "Terminal")
if hasattr(terminalclass, "_".join(['do', command])):
return terminalclass, getattr(terminalclass, "_".join(['do', command]))
raise AttributeError
def do_help(self, arg):
"""
List available commands with "help" or detailed help with "help cmd".
"""
if arg:
# XXX check arg syntax
try:
func = self.getfunc('help_' + arg)
except AttributeError:
try:
if arg[-5:] == '__pvt':
raise AttributeError
doc = self.getfunc('do_' + arg).__doc__
if doc:
self.stdout.write("%s\n" % str(doc))
return
except AttributeError:
pass
self.stdout.write("%s\n" % str(self.nohelp % (arg,)))
return
func()
else:
names = self.get_names()
cmds_doc = []
funchelp = {}
for name in names:
if name[:5] == 'help_':
funchelp[name[5:]] = 1
names.sort()
# There can be duplicates if routines overridden
prevname = ''
for name in names:
if name[:3] == 'do_' and name[-5:] != '__pvt':
if name == prevname:
continue
prevname = name
cmd = name[3:]
if cmd in funchelp:
cmds_doc.append(cmd)
del funchelp[cmd]
elif self.getfunc(name).__doc__:
cmds_doc.append(cmd)
self.stdout.write("%s\n" % str(self.doc_leader))
self.print_topics(self.doc_header, cmds_doc, 80)
self.print_topics(self.misc_header, list(funchelp.keys()), 80)
def do_exit(self, arg):
"""
Exit
"""
if arg:
print(self.prompt + "No arguments please")
print("Exiting")
time.sleep(1)
return True
def do_reload(self, arg):
if arg == "":
print("No Arguments found")
return
arg = parse(arg)
if arg[0] == "all":
localmodulelst = self.modulelst.copy()
self.modulelst = []
for i in localmodulelst:
print("Reloading", ".".join(i["name"].split(".")[1:]))
importlib.invalidate_caches()
self.reloadmoduledep(i["name"])
self.addmodule(i["pathdir"], i["importdir"])
self.autocommands = [item[3:] for item in self.get_names() if
(item[-5:] != '__pvt') and (item[:3] == 'do_')]
self.session = PromptSession(completer=WordCompleter(self.autocommands, ignore_case=False))
else:
print("Only argument \'all\' is accepted")
def do_listmodules(self, arg):
arg = parse(arg)
if arg[0] == "sys":
for i in sys.modules:
print(i)
elif len(arg) != 0:
print("No Argument Please")
else:
print("Listing all imported Modules")
for i in self.modulelst:
print(".".join(i["module"].__name__.split(".")[1:]))
So I'm trying to make a CLI but I'm having some errors with the 'cd' command. I can't access the 'target_path' variable from my cd command. Here's the code for the CLI:
import os, colorama
ERROR_COLOR = colorama.Back.RED + colorama.Fore.BLACK
user = os.getlogin()
target_path = f'C:\\Users\\{user}'
class Commands:
def __init__(self):
raise RuntimeError("Cannot instantiate class 'Commands'")
#staticmethod
def help(cmd=None):
cmd_help_msgs = {
'help': \
"""
HELP:
USAGE:
help [command: str]
DESCRIPTION:
Prints out the description and usage of a command.
If command is not passed, it prints out the help messages of all commands
""",
'cls': \
"""
CLS:
USAGE:
cls
DESCRIPTION:
Clears the terminal
""",
'exit': \
"""
EXIT:
USAGE:
exit
DESCRITPION:
Quits the program
""",
'echo': \
"""
ECHO:
USAGE:
echo <msg: str>
DESCRIPTION:
Prints out msg
"""
}
help_msg = f"""
IMPORTANT: [] = optional parameter, <> = mandatory parameter
COMMANDS:
"""
if cmd is None:
print(help_msg, end='')
for val in cmd_help_msgs.values():
print(val, end='')
else:
try:
print(help_msg, end='')
print(cmd_help_msgs[cmd])
except:
print(f"{ERROR_COLOR}'{cmd}' is not recognized as a valid command! Do 'help' to see all valid commands{colorama.Style.RESET_ALL}")
return
print('\n')
#staticmethod
def cls():
os.system('cls')
#staticmethod
def exit():
exit(0)
#staticmethod
def echo(msg=''):
if msg == '':
print(ERROR_COLOR + "Missing required argument 'msg'" + colorama.Style.RESET_ALL)
return
print(msg)
#staticmethod
def cd(directory=''):
if directory == '':
print(target_path)
return
if directory[1] != ':':
if os.path.exists(target_path + directory) and os.path.isdir(target_path + directory):
target_path += directory
else:
print(f"{directory} is not a valid directory")
else:
if os.path.exists(directory) and os.path.isdir(directory):
target_path = directory
else:
print(f"{directory} is not a valid directory")
command_list = [func for func in dir(Commands) if callable(getattr(Commands, func)) and not func.startswith('__')]
os.system('cls')
while True:
command_has_kws = False
command = input(f'[{target_path}]{colorama.Fore.CYAN} # {colorama.Fore.LIGHTBLUE_EX}').strip().split(' ')
print(colorama.Style.RESET_ALL, end='')
for i in range(len(command)):
command[i] = command[i].strip()
if command[i].startswith('-'):
command_has_kws = True
if command[0] in command_list:
try:
if not command_has_kws:
getattr(Commands, command[0])(*command[1:])
else:
# REMINDER: Type 'getattr(Commands, command[0]).__code__.co_varnames[:getattr(Commands, command[0]).__code__.co_argcount]' to get the parameters of a command
pass # TODO: Implement keywords
except TypeError:
print(f"{ERROR_COLOR}Too many or too little arguments were passed to '{command[0]}'{colorama.Style.RESET_ALL}")
else:
print(f"{ERROR_COLOR}'{command[0]}' is not recognized as a valid command! Do 'help' to see all valid commands{colorama.Style.RESET_ALL}\n")
Here's 2 things you should focus on:
target_path = f'C:\\Users\\{user}'
And:
#staticmethod
def cd(directory=''):
if directory == '':
print(target_path)
return
if directory[1] != ':':
if os.path.exists(target_path + directory) and os.path.isdir(target_path + directory):
target_path += directory
else:
print(f"{directory} is not a valid directory")
else:
if os.path.exists(directory) and os.path.isdir(directory):
target_path = directory
else:
print(f"{directory} is not a valid directory")
Also here's the error message:
Traceback (most recent call last):
File "C:\Users\[REDACTED]\Desktop\Crabby_CLI\main.py", line 132, in <module>
getattr(Commands, command[0])(*command[1:])
File "C:\Users\[REDACTED]\Desktop\Crabby_CLI\main.py", line 102, in cd
if os.path.exists(target_path + directory) and os.path.isdir(target_path + directory):
UnboundLocalError: local variable 'target_path' referenced before assignment
INFORMATION:
Interpreter - Python 3.8
OS - Windows 10
Editor - PyCharm Community Edition
local variable 'target_path' referenced before assignment this error suggests that you are trying to access target_path in one of your functions before assigning any value and if my understanding is correct, you want to use the global target_path, that you have set in top, within those functions.
If this is correct, then you need to tell your functions to use global variables.
Solution :
Within each function that uses the 'target_path' variable, in the beginning, put this line
def abc():
# add this line
global target_path
...
...
# now use `target_path` here
first to mention, my code works, I just feel, I could do it more efficient. But how?
I have a routine with (optional and standard given) arguments:
def read(fpath = 'C:', fname = 'text.txt')
Later I call this function and case by case different arguments for the shown function:
def get(index, path=None, name=None):
if path == None:
if name == None:
elements = read()
else:
elements = read(fname=name)
else:
if name == None:
elements = read(fpath=path)
else:
elements = read(fpath=path,fname=name)
How can I write it shorter without losing clarity of code?
Thank you.
PS: Is my first question, if I missed a rule, please correct me. I'm learning.
You can modify the definition of read to use the same arguments' names, then you can call it directly.
def read(path=None, name=None):
print(path)
print(name)
return 'something usefull'
def get(index, **kwargs):
elements = read(**kwargs)
get(0, path='a path', name='a name')
# a path
# a name
Instead of path=None, name=None you can of course provide actual default values in read's definition (unless they are mutable).
You could work with a dictionary and use keyword parameters:
def get(index, path = None, name = None):
kwargs = {}
if path is not None:
kwargs['path'] = path
if name is not None:
kwargs['name'] = name
elements = read(**kwargs)
Or if you simply want to filter out Nones for all parameters:
def get(index, path = None, name = None):
kwargs = dict(path = path, name = name)
elements = read(**{k:v for k,v in kwargs.items() if v is not None})
Nevertheless I wonder whether it wouldn't be better, if you communicate the default values to the user by using the same default values. Since a user will notice weird behavior. So my advice would be to use:
def read(index, fpath = 'C:', fname = 'text.txt'):
elements = read(fpath=fpath,fname=fname)
Since now a user can see from the type signature what will happen if he/she does not provide a certain parameter. You can also pass the **kwargs in which case the arguments provided to get are (with except of index here), passed to read(..):
def get(index, **kwargs):
elements = read(**kwargs)
I think you can use **kwargs over here.
**kwargs allows you to pass keyworded variable length of arguments to a function. You should use **kwargs if you want to handle named arguments in a function. Here is an example:
def greet_me(**kwargs):
if kwargs is not None:
for key, value in kwargs.iteritems():
print "%s == %s" %(key,value)
Output:
greet_me(name="yasoob")
name == yasoob
when I try to call the changeProfile function, I keep getting the error "getAuthCode is not defined" even though it is clearly defined. Why do I keep getting this error? What am I doing wrong? It used to work from what I remember, but now all of a sudden it doesn't. Any help would be appreciated :)
import os,re,json,socket,random,select,threading,time,sys
import urllib.request as urlreq
from urllib.request import quote,unquote
import urllib.parse
class miscellanous:
"""Main class."""
def __init__():
"""What to do on initalizing."""
pass
def getAuthCode(u, p):
"""Get authentification code from username and password specified."""
try:
data=urllib.request.urlopen('http://chatango.com/login',urllib.parse.urlencode({'user_id': u, 'password': p, 'storecookie': 'on', 'checkerrors': 'yes'}).encode()).headers
except Exception as e: print("Error: Auth request: %s" % e)
for x, y in data.items():
if x.lower() == 'set-cookie':
returned = re.compile(r"auth\.chatango\.com ?= ?([^;]*)", re.IGNORECASE).search(y)
if returned:
ret = returned.group(1)
if ret == "": raise ValueError("Error: Unable to get auth: Error in username/password.")
return ret
def createAccount(u, p):
"""Create an account with the username and password specified."""
try:
resp=urllib.request.urlopen("http://chatango.com/signupdir", urllib.parse.urlencode({"email": "accmaker"+str(random.randrange(1,1000000000000))+"#hotmail.com", "login": u, "password": p, "password_confirm": p, "storecookie": "on", "checkerrors": "yes"}).encode())
fu=str(resp.read())
resp.close()
if "screen name has been" in fu: r = "Error: User could not be created: Already exists."
else: r = "The user was created. If it didn't work, try another username."
return r
except: return "Error: Invalid or missing arguments."
def createGroup(u, p, g, d="Description.", m="Owner message."):
"""Creates a group with the username, password, group name, description and owner message specified."""
try:
g=g.replace(" ","-")
resp=urllib.request.urlopen("http://chatango.com/creategrouplogin",urllib.parse.urlencode({"User-Agent": "Mozilla/5.0", "uns": "0", "p": p, "lo": u, "d": d, "u": g, "n": m}).encode())
fu=str(resp.read())
resp.close()
if "groupexists" in fu: r = "Error: Group could not be created: Already exists."
else: r = "The group was created. If it didn't work, try another group name. Click <a href='http://%s.chatango.com/' target='_blank'>[[here]]<a> to get to the new group."
return r
except: return "Error: Invalid or missing arguments."
def changeProfile(u, p, m="Default", e="accmaker"+str(random.randrange(1,1000000000000))+"#hotmail.com", l="Norway", g="M", a="18"):
try:
resp = urlreq.urlopen('http://'+u.lower()+'.chatango.com/updateprofile?&d&pic&s='+getAuthCode(u, p), urllib.parse.urlencode({"show_friends": "checked", "dir": "checked", "checkerrors": "yes", "uns": "1", "line": m, "email": e, "location": l, "gender": g, "age": a}).encode())
return "The profile change was successful."
except Exception as e:
return "%s" % e
def checkOnlineStatus(u):
"""Check if the predefined username is online or offline."""
if "Chat with" in urlreq.urlopen("http://"+u.lower()+".chatango.com").read().decode(): return '<b><font color="#00ff00">Online</font></b>'
else: return "<b><font color='#ff0000'>Offline</font></b>"
resp.close()
def checkUserGender(u):
"""Get the gender for the predefined username."""
resp=urlreq.urlopen("http://st.chatango.com/profileimg/%s/%s/%s/mod1.xml" % (u.lower()[0], u.lower()[1], u.lower()))
try: ru=re.compile(r'<s>(.*?)</s>', re.IGNORECASE).search(resp.read().decode()).group(1)
except: ru="?"
ret=unquote(ru)
resp.close()
if ret=="M": r="Male"
elif ret=="F": r="Female"
elif ret=="?": r="Not specified"
return r
def changeBackGround(u, p, x, transparency=None):
"""Update the user's bg using the predefined username, password and bg color."""
color=quote(x.split()[0])
try: image=x.split()[1]
except: image=None
if color and len(color)==1:
color=color*6
if color and len(color)==3:
color+=color
elif color and len(color)!=6:
return False
if transparency!=None and abs(transparency)>1:
transparency = abs(transparency) / 100
data=urllib.request.urlopen("http://fp.chatango.com/profileimg/%s/%s/%s/msgbg.xml" % (u[0], u[1], u.lower())).read().decode()
data=dict([x.replace('"', '').split("=") for x in re.findall('(\w+=".*?")', data)[1:]])
data["p"]=p
data["lo"]=u
if color: data["bgc"] = color
if transparency!=None: data["bgalp"]=abs(transparency) * 100
if image!=None: data["useimg"]=1 if bool(image) else 0
data12=urllib.parse.urlencode(data)
data13=data12.encode()
der=urllib.request.urlopen("http://chatango.com/updatemsgbg", data13).read()
def checkIfGroupExists(g):
"""Check if the predefined group exists."""
i = urlreq.urlopen('http://'+g+'.chatango.com').read().decode()
if '<table id="group_table"' in i: return True#"This chat does exist!"
else: return False#"This chat doesn't exist."
i.close()
All of the functions you've shown are part of the miscellaneous class, so to access them, you need to prefix them with the class name. To refer to the getAuthCode function, you'd use miscellaneous.getAuthCode.
But, it doesn't seem like you should really be using a class here. You never create instances of miscellaneous, nor are the functions set up to be run as methods. So, probably the right answer is to get rid of the class miscellaneous: declaration at the top of the file, and then to unindent all the functions one level.
(Note, in Python 2 you'd have an additional issue if you kept the class: Unbound methods required that their first argument be an instance of their class (or a subclass). In that situation, you'd need to use the staticmethod decorator on any functions that did not expect to get an instance at all.)
Functions within a Python class are called methods. They normally take a self parameter before their other parameters. Furthermore, methods cannot "see" each other directly; you need to call them as self.method(args) instead of just method(args).
Imagine a class Interpreter that takes a Parser and evaluates the data yielded by the Parser into a Document. The Interpreter must be instantiated and is supposed to be executed immediately, with no reuse. The reason to not make it just a function is to be able to use sub-procedures during the interpretation phase (methods).
Below is a simple example of such an Interpreter class. I personally consider it bad to call self.run() from the constructor, but at the same time, I can not think of a better design. Can you advise?
class Interpreter():
def __init__(self, workspace, document, parser):
super().__init__()
self.workspace = workspace
self.document = document
self.parser = parser
self.run()
def run(self):
while True:
data = self.parser.next()
if not data: break
self.interpret(data)
def interpret(self, data):
if data['type'] == 'statement':
handler_name = 'statement_{0}'.format(data['name'])
method = getattr(self, handler_name, None)
if not method or not callable(method):
raise SyntaxError('unknown statement {0}'.format(data['name']))
method(data)
elif data['type'] == 'assignment':
self.document.define(data['left'], data['right'])
else:
raise RuntimeError('unexpected data from parser')
def statement_print(self, data):
print(*data['args'])
def statement_load(self, data):
if len(data['args']) not in (1, 3):
raise SyntaxError('load statement requires 1 or 3 arguments')
name = data['args'][0]
dest = name
if len(data) == 3:
if data['args'][1] != 'as':
raise SyntaxError('load statement second argument must be "as"')
dest = data['args'][2]
doc = self.workspace.load(name)
self.document.alias(doc, dest)
def statement_if(self, data):
# todo: check if if-statement applies
# ...
next = self.parser.next()
if next['indent'] <= data['indent']:
raise SyntaxError('need at least one line after if-statement')
indent = next['indent']
while next['indent'] == indent:
self.interpret(next)
next = self.parser.next()
if next['indent'] > indent:
raise SyntaxError('unexpected indent')
self.parser.push_back(next)