I have a program in python that has an ini file with the configuration variables for the program. Some variables are ReadWrite and others ReadOnly.
My config file is something like this:
[AuthCtrlr]
enable = True
nbrOfMssgs = 10
where I want the variable enable to be ReadWrite and nbrOfMssgs ReadOnly.
In my python code, I can change my variables with:
parser.set('AuthCtrlr', 'enable', False)
with the configparser module.
Is there a way to make a code where if I want to change the variable nbrOfMssgs it prints something like "This variable is ReadOnly" and don't change the value of the variable?
import configparser
class wrappedParser(configparser.ConfigParser):
def __init__(self):
super().__init__()
self.readOnlySettings = []
def set(self, category, setting, value):
if setting in self.readOnlySettings:
raise PermissionError(f"{setting} setting is read-only")
else:
return super().set(category, setting, value)
def makeReadOnly(self, setting):
self.readOnlySettings.append(setting)
config = wrappedParser()
config['bitbucket.org'] = {}
config.set('bitbucket.org', 'User', 'hg')
config.makeReadOnly('User')
try:
config.set('bitbucket.org', 'User', 'pg')
except PermissionError as err:
print(err)
print(config.get('bitbucket.org', 'User'))
Make a child class inheriting from ConfigParser that reimplements set operation in a way that raises an error if the setting you are trying to change is one of the read-only ones.
Still, this does not in any way improve security of your code. It's only good to prevent you from accidentially changing the settings. Which asks the question, why would that happen?
Related
Is it possible use a variable as a container for a Python module, and then adding another one to the same variable?
So for example, if I would have a Python file called general_config.py containing a general config of some kind:
class GeneralConfig:
attribute1 = "Some attribute"
attribute2 = "Some other attribute"
And if I would import this Python module as a variable containing a general config, I would do:
import general_config.py as Config
Then I can access its attributes by doing:
generalParameter = Config.GeneralConfig.attribute1
But what if I want to add some specific parameters to my config (say from specific_config.py), while keeping the general one as part of the entire config? So it would do something like that:
if someSpecificCondition:
Config += import specific_config.py
else:
Config += import other_config.py
While keeping the Config in the original scope? Thanks in advance.
If you want your general config to inherit your other configs for whatever reason, you could do something like this. But Tom's answer makes more sense, since there's no runtime class creation.
class BaseConfig:
att = "hello world"
def inherit_configs(some_condition):
if some_condition:
from config1 import Config1
class Config(BaseConfig, Config1):
pass
return Config
else:
from config2 import Config2
class Config(BaseConfig, Config2):
pass
return Config
config = inherit_configs(some_condition)()
I've been trying to figure out how best to set this up. Cutting it down as much as I can. I have 4 python files: core.py (main), logger_controler.py, config_controller.py, and a 4th as a module or singleton well just call it tool.py.
The way I have it setup is logging has an init function that setup pythons built in logging with the necessary levels, formatter, directory location, etc. I call this init function in main.
import logging
import logger_controller
def main():
logger_controller.init_log()
logger = logging.getLogger(__name__)
if __name__ == "__main__":
main()
config_controller is using configparser and is mainly a singleton as a controller for my config.
import configparser
import logging
logger = logging.getLogger(__name__)
class ConfigController(object):
def __init__(self, *file_names):
self.config_parser = configparser.ConfigParser()
found_files = self.config_parser.read(file_names)
if not found_files:
raise ValueError("No config file found.")
self._validate()
def _validate(self):
...
def read_config(self, section, field):
try:
data = self.config_parser.get(section, field)
except (configparser.NoSectionError, configparser.NoOptionError) as e:
logger.error(e)
data = None
return data
config = ConfigController("config.ini")
And then my problem is trying to create the 4th file and making sure both my logger and config parser are running before it. I'm also wanting this 4th one to be a singleton so it's following a similar format as the config_controller.
So tool.py uses config_controller to pull anything it needs from the config file. It also has some error checking for if config_controller's read_config returns None as that isn't validated in _validate. I did this as I wanted my logging to have a general layer for error checking and a more specific layer. So _validate just checks if required fields and sections are in the config file. Then wherever the field is read will handle extra error checking.
So my main problem is this:
How do I have it where my logger and configparser are both running and available before anything else. I'm very much willing to rework all of this, but I'd like to keep the functionality of it all.
One attempt I tried that works, but seems very messy is making my logger_controler a singleton that just returns python's logging object.
import logging
import os
class MyLogger(object):
def __new__(cls, *args, **kwargs):
init_log()
return logging
def init_log():
...
mylogger = MyLogger()
Then in core.py
from logger_controller import mylogger
logger = mylogger.getLogger(__name__)
I feel like there should be a better way to do the above, but I'm honestly not sure how.
A few ideas:
Would I be able to extend the logging class instead of just using that init_log function?
Maybe there's a way I can make all 3 individual modules such that they each initialize in a correct order? My attempts here didn't quite work as I also have some internal data that I wouldn't want exposed to classes using the module, just the functionality.
I'd like to have it where all 3, logging, configparsing, and the tool, available anywhere I import them.
How I have it setup now "works" but if I were to import the tool.py anywhere in core.py and an error occurs that I need to catch, then my logger won't be able to log it as this tool is loading before the init of my logger.
I have been tasked to modify the behavior of a function in one of our Python classes.
Our function takes a few parameters, one being a debug flag. Currently the if the debug flag is not specified then we assume it to be False. What we need it to do is when debug is not specified, check the variable "debug" from the calling code and use that value, if it exists.
I would simply change the name of the debug parameter in the function declaration, except that we have a lot of legacy code that uses that flag.
This is in Jupyter Lab, if it makes any difference.
Sample code:
class MyClass:
#classmethod
def fn(self, debug=None):
if debug is None:
try:
debug = parent.debug
except Exception as e:
print(e)
debug = "BAD"
return debug
debug = True
x = myClass
print( x.fn() )
I would want the output to be "True" but it ends up being:
global name 'parent' is not defined
BAD
Is what I am trying to do possible? If so, how?
Use globals()['debug'] instead.
Or replace your fn() method to:
#classmethod
def fn(self, debug=None):
if debug is None:
debug = globals().get('debug', 'BAD')
return debug
You access variable parent which us not defined in your code.
Firstly, the MyClass in your code isn't inherited from any other class. Technically, the class doesn't have any parent(except the obj class by default), so it's impossible to access something that doesn't exist like 'parent.debug'. Secondly, as for your problem, I'm assuming that you want to access the debug variable that is initialized outside the class then you can easily do that by doing something like this:
global debug
class MyClass:
#classmethod
def fn(self, debug=None):
if debug is None:
try:
debug = debugOut
except Exception as e:
print(e)
debug = "BAD"
return debug
debugOut = True
x = MyClass()
print(x.fn())
The above code returns True
i'm currently working on a project for dns-enumeration, which sends requests to various APIs.
Some of these APIs require an API-Key, which i provide in a config.ini file. In my current setup I use configparser to read-in the different values into an object, so i can access the object when needed. Now, as I try to implement something like a class structure, i would like to read-in the config file once in the init of a parent class, so i can inherit every tool that needs an API-Key from that class.
Right now the setup looks something like this:
class Source:
def __init__(self):
config = configparser.ConfigParser()
config.read('./config.ini')
self.config = config
class BinaryEdge(Source):
def __init__(self):
super().__init__()
def query(self, domain, dnsprobe):
api_key = self.config['BINARYEDGE']['API-KEY']
url = 'https://api.binaryedge.io/v2/query/domains/subdomain/' + domain
fqdns = []
...
In my understanding, if i initiate a new BinaryEdge-Instance, for example like this:
if __name__ == "__main__":
BinaryEdge = BinaryEdge()
print(BinaryEdge.query("heise.de", False))
It technically should read in the config file into an object and pass it to the newly created object, so i can access it via self.config, something like this:
def query(self, domain, dnsprobe):
api_key = self.config['BINARYEDGE']['API-KEY']
url = 'https://api.binaryedge.io/v2/query/domains/subdomain/' + domain
fqdns = []
...
But when im debugging this setup, the config object stays default (and threrefore empty), which obviously leads straight into a key error:
File "/usr/lib64/python3.9/configparser.py", line 960, in __getitem__
raise KeyError(key)
KeyError: 'BINARYEDGE'
As im not as good in python programming as i would like to be, i'm struggling solving this error on my own and would be thankful for any advancing input.
I figured it out myself after getting input from #Jakub Szlaur:
My file-path pointed to the wrong folders, therefore the config.ini file was never reached.
After changing:
config.read('./config.ini')
to
config.read('$HOME/$PROJECT_PATH/config.ini')
it worked as expected.
I also changed the "Source"-Class according to the comments for "better code-style":
class Source:
def __init__(self):
self.config = self.readconfig('../config.ini')
def readconfig(self, filename):
config = configparser.ConfigParser()
config.read(filename)
return config
Thanks for the help! ;-)
The code looks like it should work (I can't find any errors).
Try checking your config file to see whether there really is such a key.
But about the code itself, there are a couple of things that I would recommend changing.
First, although reading the config is part of the initialisation of Source, it would be better if you made it a function, then called that function:
class Source:
def __init__(self):
self.config = self.readconfig("./config.ini")
def readconfig(self, filename):
config = configparser.ConfigParser()
config.read(filename)
return config
Never do this: BinaryEdge = BinaryEdge(). If you wanted to make another instance of BinaryEdge, it would call the BinaryEdge variable instead. Name it something different.
I am using structlog - http://www.structlog.org/en/stable/ in my Python Project. I have one if the processors in the configuration to be
stdlib.add_logger_name
This adds the key in the event_dict to be logger. But, I want to change the key string to something else like namespace rather than logger. How can I do that?
I have checked the function for
stdlib.add_logger_name(logger, method_name, event_dict)
but that function uses hardcoded string logger as
event_dict["logger"] = logger.name
Currently, structlog.stdlib.add_logger_name() is 6 LoC, of which you most likely only need two:
def add_logger_name(logger, method_name, event_dict):
"""
Add the logger name to the event dict.
"""
record = event_dict.get("_record")
if record is None:
event_dict["logger"] = logger.name
else:
event_dict["logger"] = record.name
return event_dict
Just copy and paste it and adapt it to your needs.
It wouldn't be worth it to add options to the processor and slow it down for everybody since it didn't come up until today, but structlog has been engineered purposefully to make such customizations easy.
Thanks to hynek's answer.
I solved this by adding a local function:
def add_logger_name(logger, method_name, event_dict):
"""
Add the logger name to the event dict with namespace as the key as per logging convention
"""
record = event_dict.get("_record")
if record is None:
event_dict["namespace"] = logger.name
else:
event_dict["namespace"] = record.name
return event_dict
Setting this in the
processors=[add_logger_name,...]