falcon-toolkit 3.4.0__tar.gz → 3.4.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/PKG-INFO +1 -1
  2. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/falcon_toolkit/__init__.py +2 -1
  3. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/falcon_toolkit/common/auth.py +3 -1
  4. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/falcon_toolkit/common/auth_backends/default.py +1 -0
  5. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/falcon_toolkit/common/auth_backends/public_mssp.py +40 -20
  6. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/falcon_toolkit/common/auth_backends/public_single_cid.py +7 -5
  7. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/falcon_toolkit/common/auth_backends/utils.py +4 -3
  8. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/falcon_toolkit/common/cli.py +15 -12
  9. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/falcon_toolkit/common/config.py +5 -6
  10. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/falcon_toolkit/common/console_utils.py +6 -5
  11. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/falcon_toolkit/common/constants.py +3 -2
  12. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/falcon_toolkit/common/logging_config.py +5 -4
  13. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/falcon_toolkit/common/meta.py +1 -3
  14. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/falcon_toolkit/common/namespace.py +1 -3
  15. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/falcon_toolkit/common/utils.py +4 -4
  16. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/falcon_toolkit/containment/cli.py +62 -52
  17. falcon_toolkit-3.4.2/falcon_toolkit/containment/perform_containment.py +156 -0
  18. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/falcon_toolkit/falcon.py +45 -31
  19. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/falcon_toolkit/hosts/cli.py +27 -25
  20. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/falcon_toolkit/hosts/host_search.py +48 -51
  21. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/falcon_toolkit/maintenance_token/cli.py +73 -56
  22. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/falcon_toolkit/maintenance_token/device_tokens.py +15 -10
  23. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/falcon_toolkit/policies/cli.py +31 -33
  24. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/falcon_toolkit/policies/constants.py +1 -0
  25. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/falcon_toolkit/policies/container.py +8 -8
  26. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/falcon_toolkit/policies/describe.py +16 -13
  27. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/falcon_toolkit/shell/cli.py +58 -47
  28. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/falcon_toolkit/shell/cmd_generators/reg.py +13 -12
  29. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/falcon_toolkit/shell/parsers.py +234 -247
  30. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/falcon_toolkit/shell/prompt.py +124 -155
  31. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/falcon_toolkit/shell/refresh.py +1 -0
  32. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/falcon_toolkit/shell/utils.py +2 -1
  33. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/pyproject.toml +2 -1
  34. falcon_toolkit-3.4.0/falcon_toolkit/containment/perform_containment.py +0 -112
  35. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/LICENSE.md +0 -0
  36. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/README.md +0 -0
  37. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/falcon_toolkit/common/__init__.py +0 -0
  38. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/falcon_toolkit/common/auth_backends/__init__.py +0 -0
  39. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/falcon_toolkit/containment/__init__.py +0 -0
  40. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/falcon_toolkit/hosts/__init__.py +0 -0
  41. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/falcon_toolkit/maintenance_token/__init__.py +0 -0
  42. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/falcon_toolkit/policies/__init__.py +0 -0
  43. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/falcon_toolkit/shell/__init__.py +0 -0
  44. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/falcon_toolkit/shell/cmd_generators/__init__.py +0 -0
  45. {falcon_toolkit-3.4.0 → falcon_toolkit-3.4.2}/falcon_toolkit/shell/cmd_generators/common.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: falcon-toolkit
3
- Version: 3.4.0
3
+ Version: 3.4.2
4
4
  Summary: Toolkit to interface with CrowdStrike Falcon via the API
5
5
  Home-page: http://github.com/CrowdStrike/Falcon-Toolkit
6
6
  License: MIT
@@ -2,8 +2,9 @@
2
2
 
3
3
  A CLI for CrowdStrike Falcon, made with love by the CrowdStrike Services Team.
4
4
  """
5
+
5
6
  __all__ = [
6
- '__version__',
7
+ "__version__",
7
8
  ]
8
9
 
9
10
  from falcon_toolkit.common.meta import __version__
@@ -5,6 +5,7 @@ Each authentication backend has to be able to return a Caracara Client object, w
5
5
  hold a FalconPy authentication object. It will be possible to generate more of these backends for
6
6
  other purposes, such as for authentication against alternative API gateways.
7
7
  """
8
+
8
9
  from __future__ import annotations
9
10
  from abc import (
10
11
  abstractmethod,
@@ -17,6 +18,7 @@ from typing import (
17
18
 
18
19
  if TYPE_CHECKING:
19
20
  from caracara import Client
21
+ from click import Context
20
22
 
21
23
 
22
24
  class AuthBackend(ABC):
@@ -50,7 +52,7 @@ class AuthBackend(ABC):
50
52
  """
51
53
 
52
54
  @abstractmethod
53
- def authenticate(self) -> Client:
55
+ def authenticate(self, ctx: Context) -> Client:
54
56
  """Return a complete OAuth2 object, ready for use with FalconPy."""
55
57
 
56
58
  @abstractmethod
@@ -2,6 +2,7 @@
2
2
 
3
3
  This file configures the default authentication backends.
4
4
  """
5
+
5
6
  from falcon_toolkit.common.auth_backends.public_mssp import PublicCloudFlightControlParentCIDBackend
6
7
  from falcon_toolkit.common.auth_backends.public_single_cid import PublicCloudSingleCIDBackend
7
8
 
@@ -3,10 +3,10 @@
3
3
  This authentication backend can take public cloud API keys (US-1, US-2, EU-1), and will ask the
4
4
  user which child CID to authenticate against.
5
5
  """
6
- import os
7
6
 
8
7
  from typing import Dict, Optional
9
8
 
9
+ import click
10
10
  import keyring
11
11
 
12
12
  from caracara import Client
@@ -93,15 +93,17 @@ class PublicCloudFlightControlParentCIDBackend(AuthBackend):
93
93
  keyring.
94
94
  """
95
95
  config: Dict[str, object] = {}
96
- config['client_id']: str = self.client_id
97
- config['cloud_name']: str = self.cloud_name
98
- config['ssl_verify']: bool = self.ssl_verify
99
- config['proxy']: Dict[str, str] = self.proxy
96
+ config["client_id"] = self.client_id
97
+ config["cloud_name"] = self.cloud_name
98
+ config["ssl_verify"] = self.ssl_verify
99
+ config["proxy"] = self.proxy
100
100
 
101
101
  return config
102
102
 
103
- def authenticate(self) -> Client:
103
+ def authenticate(self, ctx: click.Context) -> Client:
104
104
  """Log the Toolkit into Falcon using the settings and keys configured at instance setup."""
105
+ chosen_cid_str = ctx.obj["cid"]
106
+
105
107
  parent_client = Client(
106
108
  client_id=self.client_id,
107
109
  client_secret=self.client_secret,
@@ -110,21 +112,39 @@ class PublicCloudFlightControlParentCIDBackend(AuthBackend):
110
112
  proxy=self.proxy,
111
113
  )
112
114
  child_cids = parent_client.flight_control.get_child_cids()
113
- chosen_cid_str = os.environ.get("FALCON_MSSP_CHILD_CID")
114
- if chosen_cid_str and chosen_cid_str.lower() in child_cids:
115
- chosen_cid = parent_client.flight_control.get_child_cid_data(
116
- cids=[chosen_cid_str]
117
- )[chosen_cid_str]
118
- else:
119
- child_cids_data = parent_client.flight_control.get_child_cid_data(cids=child_cids)
120
- chosen_cid_str = choose_cid(
121
- cids=child_cids_data,
122
- prompt_text="MSSP Child CID Search"
123
- )
124
- chosen_cid = child_cids_data[chosen_cid_str]
125
115
 
126
- chosen_cid_name = chosen_cid['name']
127
- print(f"Connecting to {chosen_cid_name}")
116
+ if chosen_cid_str and chosen_cid_str in child_cids:
117
+ click.echo(
118
+ click.style("Valid member CID ", fg="blue")
119
+ + click.style(chosen_cid_str, fg="blue", bold=True)
120
+ + click.style(" provided. Skipping CID selection.", fg="blue")
121
+ )
122
+ elif chosen_cid_str:
123
+ click.echo(click.style("An invalid CID was provided at the command line.", fg="red"))
124
+ click.echo("Please search for an alternative CID:")
125
+ # Blank out a bad value
126
+ chosen_cid_str = None
127
+
128
+ if not chosen_cid_str:
129
+ if chosen_cid_str and chosen_cid_str.lower() in child_cids:
130
+ chosen_cid = parent_client.flight_control.get_child_cid_data(
131
+ cids=[chosen_cid_str],
132
+ )[chosen_cid_str]
133
+ else:
134
+ child_cids_data = parent_client.flight_control.get_child_cid_data(cids=child_cids)
135
+ if not child_cids_data:
136
+ raise RuntimeError(
137
+ "No child CIDs accessible. Please check your API credentials."
138
+ )
139
+
140
+ chosen_cid_str = choose_cid(
141
+ cids=child_cids_data,
142
+ prompt_text="MSSP Child CID Search",
143
+ )
144
+ chosen_cid = child_cids_data[chosen_cid_str]
145
+
146
+ chosen_cid_name = chosen_cid["name"]
147
+ print(f"Connecting to {chosen_cid_name}")
128
148
 
129
149
  client = Client(
130
150
  client_id=self.client_id,
@@ -3,8 +3,10 @@
3
3
  This authentication backend can take public cloud API keys (US-1, US-2, EU-1), and will return
4
4
  an OAuth2 object suitable for authenticating with FalconPy.
5
5
  """
6
+
6
7
  from typing import Dict, Optional
7
8
 
9
+ import click
8
10
  import keyring
9
11
 
10
12
  from caracara import Client
@@ -81,14 +83,14 @@ class PublicCloudSingleCIDBackend(AuthBackend):
81
83
  keyring.
82
84
  """
83
85
  config: Dict[str, object] = {}
84
- config['client_id'] = self.client_id
85
- config['cloud_name'] = self.cloud_name
86
- config['ssl_verify'] = self.ssl_verify
87
- config['proxy'] = self.proxy
86
+ config["client_id"] = self.client_id
87
+ config["cloud_name"] = self.cloud_name
88
+ config["ssl_verify"] = self.ssl_verify
89
+ config["proxy"] = self.proxy
88
90
 
89
91
  return config
90
92
 
91
- def authenticate(self) -> Client:
93
+ def authenticate(self, ctx: click.Context) -> Client:
92
94
  """Log the Toolkit into Falcon using the settings and keys configured at instance setup."""
93
95
  client = Client(
94
96
  client_id=self.client_id,
@@ -8,6 +8,7 @@ This file provides:
8
8
  - A cloud selection function to allow a user to choose a cloud via Prompt Toolkit
9
9
  - Advanced options configuration for overriding cloud, TLS validation, etc.
10
10
  """
11
+
11
12
  from typing import (
12
13
  Dict,
13
14
  List,
@@ -56,8 +57,8 @@ class AdvancedOptionsType(NamedTuple):
56
57
  def advanced_options_wizard() -> AdvancedOptionsType:
57
58
  """Define advanced connection options and return an AdvancedOptionsType."""
58
59
  advanced_options_input = fancy_input("Do you want to configure more options? [y/n]: ")
59
- if advanced_options_input not in ('y', 'Y'):
60
- return AdvancedOptionsType('auto', True, None)
60
+ if advanced_options_input not in ("y", "Y"):
61
+ return AdvancedOptionsType("auto", True, None)
61
62
 
62
63
  cloud_name = cloud_choice()
63
64
 
@@ -76,7 +77,7 @@ def advanced_options_wizard() -> AdvancedOptionsType:
76
77
  proxy_url_input = fancy_input("HTTPS proxy URL (leave blank if not needed): ", loop=False)
77
78
 
78
79
  if proxy_url_input:
79
- proxy_dict = {'https', proxy_url_input}
80
+ proxy_dict = {"https", proxy_url_input}
80
81
 
81
82
  advanced_options_result = AdvancedOptionsType(cloud_name, ssl_verify, proxy_dict)
82
83
  return advanced_options_result
@@ -1,4 +1,5 @@
1
1
  """Falcon Toolkit: Common CLI Functions."""
2
+
2
3
  import sys
3
4
 
4
5
  from typing import List
@@ -13,25 +14,27 @@ from falcon_toolkit.common.config import FalconToolkitConfig
13
14
 
14
15
  def get_instance(ctx: click.Context):
15
16
  """Load a specified Falcon instance configuration ready for use."""
16
- config: FalconToolkitConfig = ctx.obj['config']
17
- profile_name: str = ctx.obj['profile_name']
17
+ config: FalconToolkitConfig = ctx.obj["config"]
18
+ profile_name: str = ctx.obj["profile_name"]
18
19
 
19
20
  if profile_name in config.instances:
20
21
  profile = config.instances[profile_name]
21
22
  elif not profile_name and len(config.instances) == 1:
22
23
  profile = list(config.instances.values())[0]
23
24
  elif not config.instances:
24
- click.echo(click.style("No profiles are configured. Please set one up first.", fg='red'))
25
+ click.echo(click.style("No profiles are configured. Please set one up first.", fg="red"))
25
26
  sys.exit(1)
26
27
  elif not profile_name:
27
- click.echo(click.style(
28
- "Multiple profiles are configured, so you must use the -p/--profile option "
29
- "to choose a profile to execute this tool with.",
30
- fg='red',
31
- ))
28
+ click.echo(
29
+ click.style(
30
+ "Multiple profiles are configured, so you must use the -p/--profile option "
31
+ "to choose a profile to execute this tool with.",
32
+ fg="red",
33
+ )
34
+ )
32
35
  sys.exit(1)
33
36
  else:
34
- click.echo(click.style(f"The profile {profile_name} does not exist.", fg='red'))
37
+ click.echo(click.style(f"The profile {profile_name} does not exist.", fg="red"))
35
38
  sys.exit(1)
36
39
 
37
40
  return profile
@@ -39,13 +42,13 @@ def get_instance(ctx: click.Context):
39
42
 
40
43
  def parse_cli_filters(filter_kv_strings: List[str], client: Client) -> FalconFilter:
41
44
  """Parse consecutive chains of -f filters into a FalconFilter object for later use."""
42
- filters = client.FalconFilter(dialect='hosts')
45
+ filters = client.FalconFilter(dialect="hosts")
43
46
  for filter_kv_string in filter_kv_strings:
44
- if '=' not in filter_kv_string:
47
+ if "=" not in filter_kv_string:
45
48
  raise ValueError("Filter key=value string is in the wrong format")
46
49
  first_equals = filter_kv_string.index("=")
47
50
  filter_key = filter_kv_string[0:first_equals]
48
- filter_value = filter_kv_string[first_equals + 1:]
51
+ filter_value = filter_kv_string[first_equals + 1 :]
49
52
  filters.create_new_filter_from_kv_string(filter_key, filter_value)
50
53
 
51
54
  return filters
@@ -4,6 +4,7 @@ Each instance configuration contains the logic required to log in to Falcon.
4
4
  These configurations are stored in a wider config file stored within the configuration
5
5
  path (usually ~/FalconToolkit/FalconToolkit.json).
6
6
  """
7
+
7
8
  from __future__ import annotations
8
9
  import importlib.util
9
10
  import json
@@ -63,9 +64,7 @@ class FalconInstanceConfig:
63
64
  break
64
65
 
65
66
  if not matching_auth_backend:
66
- raise ValueError(
67
- f"Auth backend {auth_backend_name} is not loaded or does not exist"
68
- )
67
+ raise ValueError(f"Auth backend {auth_backend_name} is not loaded or does not exist")
69
68
 
70
69
  auth_backend_config = auth.get("backend_config")
71
70
  if not auth_backend_config:
@@ -81,7 +80,7 @@ class FalconInstanceConfig:
81
80
  "auth": {
82
81
  "backend_name": self.auth_backend.simple_name,
83
82
  "backend_config": self.auth_backend.dump_config(),
84
- }
83
+ },
85
84
  }
86
85
 
87
86
 
@@ -113,7 +112,7 @@ class FalconToolkitConfig:
113
112
  """Load a Falcon Toolkit configuration file from disk."""
114
113
  self.config_file_path = os.path.join(config_path, CONFIG_FILENAME)
115
114
  if os.path.exists(self.config_file_path):
116
- with open(self.config_file_path, 'rb') as config_file_handle:
115
+ with open(self.config_file_path, "rb") as config_file_handle:
117
116
  config_data = json.load(config_file_handle)
118
117
  else:
119
118
  config_data = {}
@@ -139,7 +138,7 @@ class FalconToolkitConfig:
139
138
  "auth_backends": self.additional_auth_backend_paths,
140
139
  "instances": [x.dump_config() for x in list(self.instances.values())],
141
140
  }
142
- with open(self.config_file_path, 'w', encoding='utf8') as config_file_handle:
141
+ with open(self.config_file_path, "w", encoding="utf8") as config_file_handle:
143
142
  json.dump(config_data, config_file_handle, sort_keys=True, indent=4)
144
143
 
145
144
  def add_instance(self):
@@ -3,12 +3,13 @@
3
3
  This file contains tools to help with displaying data on the console in a user-friendly,
4
4
  colourful and/or helpful way.
5
5
  """
6
+
6
7
  import platform
7
8
 
8
9
 
9
- ESC = '\033'
10
- OSC = ESC + ']'
11
- ST = ESC + '\\'
10
+ ESC = "\033"
11
+ OSC = ESC + "]"
12
+ ST = ESC + "\\"
12
13
 
13
14
 
14
15
  def build_hyperlink(target: str, text: str, link_id: str = None):
@@ -17,7 +18,7 @@ def build_hyperlink(target: str, text: str, link_id: str = None):
17
18
  id_str = "id=" + link_id
18
19
  else:
19
20
  id_str = ""
20
- return f'{OSC}8;{id_str};{target}{ST}{text}{OSC}8;;{ST}'
21
+ return f"{OSC}8;{id_str};{target}{ST}{text}{OSC}8;;{ST}"
21
22
 
22
23
 
23
24
  def build_file_hyperlink(file_path: str, text: str, link_id: str = None):
@@ -27,7 +28,7 @@ def build_file_hyperlink(file_path: str, text: str, link_id: str = None):
27
28
  else:
28
29
  hostname = platform.node()
29
30
 
30
- if file_path.startswith('/'):
31
+ if file_path.startswith("/"):
31
32
  file_path = file_path[1:]
32
33
 
33
34
  uri_file_path = f"file://{hostname}/{file_path}"
@@ -1,4 +1,5 @@
1
1
  """Falcon Toolkit: Common Constants."""
2
+
2
3
  import os
3
4
 
4
5
  import platformdirs
@@ -16,6 +17,6 @@ OLD_DEFAULT_CONFIG_DIR = os.path.expanduser("~/FalconToolkit")
16
17
 
17
18
  # Logging
18
19
  LOG_CONSOLE_FORMATTER = "%(message)s"
19
- LOG_FILE_DATE_FORMAT = '%Y-%m-%dT%H:%M:%S%z'
20
- LOG_FILE_OUTPUT_FORMAT = '%(asctime)s %(name)-24s %(levelname)-8s %(message)s'
20
+ LOG_FILE_DATE_FORMAT = "%Y-%m-%dT%H:%M:%S%z"
21
+ LOG_FILE_OUTPUT_FORMAT = "%(asctime)s %(name)-24s %(levelname)-8s %(message)s"
21
22
  LOG_SUB_DIR = "logs"
@@ -4,6 +4,7 @@ The toolkit provides extremely detailed logging. This class configures the globa
4
4
  capture logs from both the toolkit itself, as well as Caracara behind the scenes.
5
5
  All logs are compressed using gzip to keep file sizes down.
6
6
  """
7
+
7
8
  import datetime
8
9
  import gzip
9
10
  import logging
@@ -48,7 +49,7 @@ def configure_logger(
48
49
  # Gzip compress the main log file on the fly via a memory stream.
49
50
  # This is required to avoid the gzip file from being corrupted if the
50
51
  # the script ends prematurely (e.g, it crashes).
51
- log_output_file = gzip.open(log_filepath, mode='wt', encoding='utf-8')
52
+ log_output_file = gzip.open(log_filepath, mode="wt", encoding="utf-8")
52
53
  stream_handler = logging.StreamHandler(log_output_file)
53
54
  memory_handler = logging.handlers.MemoryHandler(
54
55
  capacity=1024,
@@ -60,7 +61,7 @@ def configure_logger(
60
61
  datefmt=LOG_FILE_DATE_FORMAT,
61
62
  format=LOG_FILE_OUTPUT_FORMAT,
62
63
  handlers=[memory_handler],
63
- encoding='utf-8',
64
+ encoding="utf-8",
64
65
  )
65
66
  else:
66
67
  logging.basicConfig(
@@ -68,14 +69,14 @@ def configure_logger(
68
69
  format=LOG_FILE_OUTPUT_FORMAT,
69
70
  datefmt=LOG_FILE_DATE_FORMAT,
70
71
  filename=log_filepath,
71
- filemode='wt',
72
+ filemode="wt",
72
73
  )
73
74
 
74
75
  console = logging.StreamHandler()
75
76
  console.setLevel(level=log_level)
76
77
  console_formatter = logging.Formatter(LOG_CONSOLE_FORMATTER)
77
78
  console.setFormatter(console_formatter)
78
- logging.getLogger('').addHandler(console)
79
+ logging.getLogger("").addHandler(console)
79
80
 
80
81
  logging.debug("Logger configured")
81
82
  return log_filepath_base
@@ -6,6 +6,4 @@ This is where all functions and variable setting that are based on application m
6
6
  import pkg_resources
7
7
 
8
8
  # Derive the version via pkg_resources, which is populated based on the version in pyproject.toml
9
- __version__ = pkg_resources.get_distribution(
10
- __name__.split(".", maxsplit=1)[0]
11
- ).version
9
+ __version__ = pkg_resources.get_distribution(__name__.split(".", maxsplit=1)[0]).version
@@ -38,9 +38,7 @@ class FalconRecursiveNamespace:
38
38
 
39
39
  def __setitem__(self, key, value):
40
40
  """Assign a key to a value in the namespace."""
41
- self.__init__(
42
- ** {key: value}
43
- )
41
+ self.__init__(**{key: value})
44
42
 
45
43
  def __delitem__(self, key):
46
44
  """Delete an item from the namespace."""
@@ -3,6 +3,7 @@
3
3
  This file is a catch-all for small code snippets that can be shared across the various sub-modules
4
4
  of the application.
5
5
  """
6
+
6
7
  import os
7
8
 
8
9
  from typing import Dict, Iterable
@@ -21,8 +22,7 @@ from falcon_toolkit.common.constants import LOG_SUB_DIR
21
22
  def fancy_input(prompt_text: str, loop: bool = True):
22
23
  """Request user input (with colour). Optionally loop until the input is not blank."""
23
24
  inputted = False
24
- colour_prompt = Style.BRIGHT + Fore.BLUE + \
25
- prompt_text + Fore.RESET + Style.RESET_ALL
25
+ colour_prompt = Style.BRIGHT + Fore.BLUE + prompt_text + Fore.RESET + Style.RESET_ALL
26
26
 
27
27
  while not inputted:
28
28
  data = input(colour_prompt)
@@ -62,11 +62,11 @@ def filename_safe_string(unsafe_string: str) -> str:
62
62
  This function is heavily inspired by https://stackoverflow.com/a/7406369.
63
63
  """
64
64
  safe_string = "".join(
65
- [c for c in unsafe_string if c.isalpha() or c.isdigit() or c == ' ']
65
+ [c for c in unsafe_string if c.isalpha() or c.isdigit() or c == " "]
66
66
  ).rstrip()
67
67
 
68
68
  # Replace spaces with underscores to match the general format of the log filename
69
- clean_string = safe_string.replace(' ', '_')
69
+ clean_string = safe_string.replace(" ", "_")
70
70
 
71
71
  return clean_string
72
72