Hi,
In my top level program file, main.py, I have
def main_function():
parser = argparse.ArgumentParser(description="my prog")
...
args = parser.parse_args()
config = configparser.ConfigParser()
if args.config_file is None:
config_file = DEFAULT_CONFIG_FILE
else:
config_file = args.config_file
config.read(config_file)
logging.config.fileConfig(fname=config_file)
logger = logging.getLogger(__name__)
do_some_stuff()
my_class_instance = myprog.MyClass()
def do_some_stuff():
logger.info("Doing stuff")
This does not work, because 'logger' is not known in the function 'do_some_stuff'.
However, if in 'my_prog/my_class.py' I have
class MyClass:
def __init__(self):
logger.debug("created instance of MyClass")
this 'just works'.
logger = logging.getLogger(__name__)
to 'do_some_stuff', but why is this necessary in this case but not in
the class?
print(name)def use_name():
name = "Loris"def define_name():
Traceback (most recent call last):use_name()
global namedef define_name():
Peterdefine_name()
use_name()
Hi,
In my top level program file, main.py, I have
def main_function():
parser = argparse.ArgumentParser(description="my prog")
...
args = parser.parse_args()
config = configparser.ConfigParser()
if args.config_file is None:
config_file = DEFAULT_CONFIG_FILE
else:
config_file = args.config_file
config.read(config_file)
logging.config.fileConfig(fname=config_file)
logger = logging.getLogger(__name__)
do_some_stuff()
my_class_instance = myprog.MyClass()
def do_some_stuff():
logger.info("Doing stuff")
This does not work, because 'logger' is not known in the function 'do_some_stuff'.
However, if in 'my_prog/my_class.py' I have
class MyClass:
def __init__(self):
logger.debug("created instance of MyClass")
this 'just works'.
I can add
logger = logging.getLogger(__name__)
to 'do_some_stuff', but why is this necessary in this case but not in
the class?
Or should I be doing this entirely differently?
On 31/03/2023 15:01, Loris Bennett wrote:[snip (53 lines)]
Your problem has nothing to do with logging -- it's about visibility ("scope") of names:
print(name)def use_name():
name = "Loris"def define_name():
Traceback (most recent call last):use_name()
File "<pyshell#56>", line 1, in <module>
use_name()
File "<pyshell#52>", line 2, in use_name
print(name)
NameError: name 'name' is not defined
Binding (=assigning to) a name inside a function makes it local to that function. If you want a global (module-level) name you have to say so:
global namedef define_name():
name = "Peter"
Peterdefine_name()
use_name()
On 01/04/2023 02.01, Loris Bennett wrote:
Hi,
In my top level program file, main.py, I have
def main_function():
parser = argparse.ArgumentParser(description="my prog")
...
args = parser.parse_args()
config = configparser.ConfigParser()
if args.config_file is None:
config_file = DEFAULT_CONFIG_FILE
else:
config_file = args.config_file
config.read(config_file)
logging.config.fileConfig(fname=config_file)
logger = logging.getLogger(__name__)
do_some_stuff()
my_class_instance = myprog.MyClass()
def do_some_stuff():
logger.info("Doing stuff")
This does not work, because 'logger' is not known in the function
'do_some_stuff'.
However, if in 'my_prog/my_class.py' I have
class MyClass:
def __init__(self):
logger.debug("created instance of MyClass")
this 'just works'.
I can add
logger = logging.getLogger(__name__)
to 'do_some_stuff', but why is this necessary in this case but not
in
the class?
Or should I be doing this entirely differently?
Yes: differently.
To complement @Peter's response, two items for consideration:
1 once main_function() has completed, have it return logger and other
such values/constructs. The target-identifiers on the LHS of the function-call will thus be within the global scope.
2 if the purposes of main_function() are condensed-down to a few
(English, or ..., language) phrases, the word "and" will feature, eg
- configure env according to cmdLN args,
- establish log(s),
- do_some_stuff(), ** AND **
- instantiate MyClass.
If these (and do_some_stuff(), like MyClass' methods) were split into separate functions* might you find it easier to see them as separate sub-solutions? Each sub-solution would be able to contribute to the
whole - the earlier ones as creating (outputting) a description,
constraint, or basis; which becomes input to a later function/method.
* there is some debate amongst developers about whether "one function,
one purpose" should be a rule, a convention, or tossed in the
trash. YMMV!
Personal view: SOLID's "Single" principle applies: there should be
only one reason (hanging over the head of each method/function, like
the Sword of Damocles) for it to change - or one 'user' who could
demand a change to that function. In other words, an updated cmdLN
option shouldn't affect a function which establishes logging, for
example.
Web.Refs: https://people.engr.tamu.edu/choe/choe/courses/20fall/315/lectures/slide23-solid.pdf
https://www.hanselminutes.com/145/solid-principles-with-uncle-bob-robert-c-martin
https://idioms.thefreedictionary.com/sword+of+Damocles https://en.wikipedia.org/wiki/Damocles
dn <PythonList@DancesWithMice.info> writes:
On 01/04/2023 02.01, Loris Bennett wrote:
Hi,
In my top level program file, main.py, I have
def main_function():
parser = argparse.ArgumentParser(description="my prog")
...
args = parser.parse_args()
config = configparser.ConfigParser()
if args.config_file is None:
config_file = DEFAULT_CONFIG_FILE
else:
config_file = args.config_file
config.read(config_file)
logging.config.fileConfig(fname=config_file)
logger = logging.getLogger(__name__)
do_some_stuff()
my_class_instance = myprog.MyClass()
def do_some_stuff():
logger.info("Doing stuff")
This does not work, because 'logger' is not known in the function
'do_some_stuff'.
However, if in 'my_prog/my_class.py' I have
class MyClass:
def __init__(self):
logger.debug("created instance of MyClass")
this 'just works'.
I can add
logger = logging.getLogger(__name__)
to 'do_some_stuff', but why is this necessary in this case but not
in
the class?
Or should I be doing this entirely differently?
Yes: differently.
To complement @Peter's response, two items for consideration:
1 once main_function() has completed, have it return logger and other
such values/constructs. The target-identifiers on the LHS of the
function-call will thus be within the global scope.
2 if the purposes of main_function() are condensed-down to a few
(English, or ..., language) phrases, the word "and" will feature, eg
- configure env according to cmdLN args,
- establish log(s),
- do_some_stuff(), ** AND **
- instantiate MyClass.
If these (and do_some_stuff(), like MyClass' methods) were split into
separate functions* might you find it easier to see them as separate
sub-solutions? Each sub-solution would be able to contribute to the
whole - the earlier ones as creating (outputting) a description,
constraint, or basis; which becomes input to a later function/method.
So if I want to modify the logging via the command line I might have the following:
---------------------------------------------------------------------
#!/usr/bin/env python3
import argparse
import logging
def get_logger(log_level):
"""Get global logger"""
logger = logging.getLogger('example')
logger.setLevel(log_level)
ch = logging.StreamHandler()
formatter = logging.Formatter('%(levelname)s - %(message)s')
ch.setFormatter(formatter)
logger.addHandler(ch)
return logger
def do_stuff():
"""Do some stuff"""
# logger.info("Doing stuff!")
def main():
"""Main"""
parser = argparse.ArgumentParser()
parser.add_argument("--log-level", dest="log_level", type=int)
args = parser.parse_args()
print(f"log level: {args.log_level}")
logger = get_logger(args.log_level)
logger.debug("Logger!")
do_stuff()
if __name__ == "__main__":--
main()
---------------------------------------------------------------------
How can I get logging for 'do_stuff' in this case without explicitly
passing 'logger' as an argument or using 'global'?
Somehow I am failing to understand how to get 'logger' defined
sufficiently high up in the program that all references 'lower down' in
the program will be automatically resolved.
* there is some debate amongst developers about whether "one function,
one purpose" should be a rule, a convention, or tossed in the
trash. YMMV!
Personal view: SOLID's "Single" principle applies: there should be
only one reason (hanging over the head of each method/function, like
the Sword of Damocles) for it to change - or one 'user' who could
demand a change to that function. In other words, an updated cmdLN
option shouldn't affect a function which establishes logging, for
example.
Web.Refs:
https://people.engr.tamu.edu/choe/choe/courses/20fall/315/lectures/slide23-solid.pdf
https://www.hanselminutes.com/145/solid-principles-with-uncle-bob-robert-c-martin
https://idioms.thefreedictionary.com/sword+of+Damocles
https://en.wikipedia.org/wiki/Damocles
I don't really get the "one reason" idea and the Sword of Damocles
analogy. The later to me is more like "there's always a downside",
since the perks of being king may mean someone might try to usurp the
throne and kill you. Where is the "single principle" aspect?
However, the idea of "one responsibility" in the sense of "do only one
thing" seems relatively clear, especially if I think in terms of writing
unit tests.
Cheers,
Loris
"Loris Bennett" <loris.bennett@fu-berlin.de> writes:
dn <PythonList@DancesWithMice.info> writes:
On 01/04/2023 02.01, Loris Bennett wrote:
Hi,
In my top level program file, main.py, I have
def main_function():
parser = argparse.ArgumentParser(description="my prog")
...
args = parser.parse_args()
config = configparser.ConfigParser()
if args.config_file is None:
config_file = DEFAULT_CONFIG_FILE
else:
config_file = args.config_file
config.read(config_file)
logging.config.fileConfig(fname=config_file)
logger = logging.getLogger(__name__)
do_some_stuff()
my_class_instance = myprog.MyClass()
def do_some_stuff():
logger.info("Doing stuff")
This does not work, because 'logger' is not known in the function
'do_some_stuff'.
However, if in 'my_prog/my_class.py' I have
class MyClass:
def __init__(self):
logger.debug("created instance of MyClass")
this 'just works'.
I can add
logger = logging.getLogger(__name__)
to 'do_some_stuff', but why is this necessary in this case but not
in
the class?
Or should I be doing this entirely differently?
Yes: differently.
To complement @Peter's response, two items for consideration:
1 once main_function() has completed, have it return logger and other
such values/constructs. The target-identifiers on the LHS of the
function-call will thus be within the global scope.
2 if the purposes of main_function() are condensed-down to a few
(English, or ..., language) phrases, the word "and" will feature, eg
- configure env according to cmdLN args,
- establish log(s),
- do_some_stuff(), ** AND **
- instantiate MyClass.
If these (and do_some_stuff(), like MyClass' methods) were split into
separate functions* might you find it easier to see them as separate
sub-solutions? Each sub-solution would be able to contribute to the
whole - the earlier ones as creating (outputting) a description,
constraint, or basis; which becomes input to a later function/method.
So if I want to modify the logging via the command line I might have the
following:
---------------------------------------------------------------------
#!/usr/bin/env python3
import argparse
import logging
def get_logger(log_level):
"""Get global logger"""
logger = logging.getLogger('example')
logger.setLevel(log_level)
ch = logging.StreamHandler()
formatter = logging.Formatter('%(levelname)s - %(message)s')
ch.setFormatter(formatter)
logger.addHandler(ch)
return logger
def do_stuff():
"""Do some stuff"""
# logger.info("Doing stuff!")
Looks like I just need
logger = logging.getLogger('example)
logger.info("Doing stuff!")
def main():
"""Main"""
parser = argparse.ArgumentParser()
parser.add_argument("--log-level", dest="log_level", type=int)
args = parser.parse_args()
print(f"log level: {args.log_level}")
logger = get_logger(args.log_level)
logger.debug("Logger!")
do_stuff()
if __name__ == "__main__":
main()
---------------------------------------------------------------------
How can I get logging for 'do_stuff' in this case without explicitly
passing 'logger' as an argument or using 'global'?
Somehow I am failing to understand how to get 'logger' defined
sufficiently high up in the program that all references 'lower down' in
the program will be automatically resolved.
* there is some debate amongst developers about whether "one function,
one purpose" should be a rule, a convention, or tossed in the
trash. YMMV!
Personal view: SOLID's "Single" principle applies: there should be
only one reason (hanging over the head of each method/function, like
the Sword of Damocles) for it to change - or one 'user' who could
demand a change to that function. In other words, an updated cmdLN
option shouldn't affect a function which establishes logging, for
example.
Web.Refs:
https://people.engr.tamu.edu/choe/choe/courses/20fall/315/lectures/slide23-solid.pdf
https://www.hanselminutes.com/145/solid-principles-with-uncle-bob-robert-c-martin
https://idioms.thefreedictionary.com/sword+of+Damocles
https://en.wikipedia.org/wiki/Damocles
I don't really get the "one reason" idea and the Sword of Damocles
analogy. The later to me is more like "there's always a downside",
since the perks of being king may mean someone might try to usurp the
throne and kill you. Where is the "single principle" aspect?
However, the idea of "one responsibility" in the sense of "do only one
thing" seems relatively clear, especially if I think in terms of writing
unit tests.
Sysop: | Keyop |
---|---|
Location: | Huddersfield, West Yorkshire, UK |
Users: | 300 |
Nodes: | 16 (2 / 14) |
Uptime: | 67:33:29 |
Calls: | 6,712 |
Files: | 12,244 |
Messages: | 5,356,419 |