Skip to content

Commit 7a5a5fd

Browse files
committed
Migrate configuration from INI to YAML
1 parent fbfb885 commit 7a5a5fd

7 files changed

Lines changed: 94 additions & 90 deletions

File tree

.dockerignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ certs/*.pem
1010
!certs/ca/*
1111

1212
tests/
13-
config.ini.example
13+
config.yaml.example
1414

1515
.github
1616
.bumpversion.cfg

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
- Update dependencies
77
- Change Flake8, Black and Bandit for Ruff
88
- Move benchmarking tool (https://github.com/pyproxytools/pyproxy-benchmark)
9+
- Change configuration file format to yaml
910

1011
## [0.4.6] - 2025-06-12
1112
### Added

config.ini.example

Lines changed: 0 additions & 44 deletions
This file was deleted.

config.yaml.example

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
server:
2+
host: "0.0.0.0"
3+
port: 8080
4+
5+
logging:
6+
debug: false
7+
access_log: "./logs/access.log"
8+
block_log: "./logs/block.log"
9+
no_logging_access: false
10+
no_logging_block: false
11+
console_format: "date=%(asctime)s level=%(levelname)s file=%(filename)s function=%(funcName)s message=%(message)s"
12+
access_log_format: "date=%(asctime)s ip_src=%(ip_src)s url=%(url)s method=%(method)s domain=%(domain)s port=%(port)s protocol=%(protocol)s bytes_sent=%(bytes_sent)s bytes_received=%(bytes_received)s tls_version=%(tls_version)s"
13+
block_log_format: "date=%(asctime)s ip_src=%(ip_src)s url=%(url)s method=%(method)s domain=%(domain)s port=%(port)s protocol=%(protocol)s"
14+
datefmt: "%Y-%m-%d %H:%M:%S"
15+
16+
files:
17+
html_403: "assets/403.html"
18+
19+
filtering:
20+
no_filter: false
21+
filter_mode: "local"
22+
blocked_sites: "config/blocked_sites.txt"
23+
blocked_url: "config/blocked_url.txt"
24+
25+
options:
26+
shortcuts: "config/shortcuts.txt"
27+
custom_header: "config/custom_header.json"
28+
authorized_ips: "config/authorized_ips.txt"
29+
30+
security:
31+
ssl_inspect: false
32+
inspect_ca_cert: "certs/ca/cert.pem"
33+
inspect_ca_key: "certs/ca/key.pem"
34+
inspect_certs_folder: "certs/"
35+
cancel_inspect: "config/cancel_inspect.txt"
36+
37+
monitoring:
38+
flask_port: 5000
39+
flask_pass: "password"
40+
41+
proxy:
42+
proxy_enable: false
43+
proxy_host: "127.0.0.1"
44+
proxy_port: 8081

docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ services:
1414
#- ./certs/ca:/app/certs/ca
1515
#- ./config:/app/config
1616
#- ./logs:/app/logs
17-
#- ./config.ini:/app/config.ini
17+
#- ./config.yaml:/app/config.yaml
1818
environment:
1919
PYPROXY_HOST: 0.0.0.0
2020
PYPROXY_PORT: 8080

pyproxy/pyproxy.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,10 @@ def main():
4949
port=get_config_value(args, config, "proxy_port", "Proxy", 8081),
5050
)
5151

52-
console_format = config.get("Logging", "console_format", fallback=None)
53-
access_log_format = config.get("Logging", "access_log_format", fallback=None)
54-
block_log_format = config.get("Logging", "block_log_format", fallback=None)
55-
datefmt = config.get("Logging", "datefmt", fallback=None)
52+
console_format = get_config_value(args, config, "console_format", "Logging", None)
53+
access_log_format = get_config_value(args, config, "access_log_format", "Logging", None)
54+
block_log_format = get_config_value(args, config, "block_log_format", "Logging", None)
55+
datefmt = get_config_value(args, config, "datefmt", "Logging", None)
5656

5757
logger_config = ProxyConfigLogger(
5858
access_log=get_config_value(args, config, "access_log", "Logging", "logs/access.log"),

pyproxy/utils/args.py

Lines changed: 43 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44
This module allows you to read the program configuration file and return the values.
55
"""
66

7-
import configparser
87
import argparse
98
import os
9+
import yaml
10+
from typing import Any
1011
from rich_argparse import MetavarTypeRichHelpFormatter
1112
from pyproxy import __version__
1213

@@ -27,49 +28,49 @@ def parse_args() -> argparse.Namespace:
2728
)
2829
parser.add_argument(
2930
"-v", "--version", action="version", version=__version__, help="Show version"
30-
) # noqa: E501
31+
)
3132
parser.add_argument("--debug", action="store_true", help="Enable debug logging")
3233
parser.add_argument("-H", "--host", type=str, help="IP address to listen on")
3334
parser.add_argument("-P", "--port", type=int, help="Port to listen on")
3435
parser.add_argument(
3536
"-f",
3637
"--config-file",
3738
type=str,
38-
default="./config.ini",
39-
help="Path to config.ini file",
40-
) # noqa: E501
39+
default="./config.yaml",
40+
help="Path to config.yaml file",
41+
)
4142
parser.add_argument("--access-log", type=str, help="Path to the access log file")
4243
parser.add_argument("--block-log", type=str, help="Path to the block log file")
4344
parser.add_argument("--html-403", type=str, help="Path to the custom 403 Forbidden HTML page")
4445
parser.add_argument("--no-filter", action="store_true", help="Disable URL and domain filtering")
4546
parser.add_argument(
4647
"--filter-mode", type=str, choices=["local", "http"], help="Filter list mode"
47-
) # noqa: E501
48+
)
4849
parser.add_argument(
4950
"--blocked-sites",
5051
type=str,
5152
help="Path to the text file containing the list of sites to block",
52-
) # noqa: E501
53+
)
5354
parser.add_argument(
5455
"--blocked-url",
5556
type=str,
5657
help="Path to the text file containing the list of URLs to block",
57-
) # noqa: E501
58+
)
5859
parser.add_argument(
5960
"--shortcuts",
6061
type=str,
6162
help="Path to the text file containing the list of shortcuts",
62-
) # noqa: E501
63+
)
6364
parser.add_argument(
6465
"--custom-header",
6566
type=str,
6667
help="Path to the json file containing the list of custom headers",
67-
) # noqa: E501
68+
)
6869
parser.add_argument(
6970
"--authorized-ips",
7071
type=str,
7172
help="Path to the txt file containing the list of authorized ips",
72-
) # noqa: E501
73+
)
7374
parser.add_argument("--no-logging-access", action="store_true", help="Disable access logging")
7475
parser.add_argument("--no-logging-block", action="store_true", help="Disable block logging")
7576
parser.add_argument("--ssl-inspect", action="store_true", help="Enable SSL inspection")
@@ -79,12 +80,12 @@ def parse_args() -> argparse.Namespace:
7980
"--inspect-certs-folder",
8081
type=str,
8182
help="Path to the generated certificates folder",
82-
) # noqa: E501
83+
)
8384
parser.add_argument(
8485
"--cancel-inspect",
8586
type=str,
8687
help="Path to the text file containing the list of URLs without ssl inspection",
87-
) # noqa: E501
88+
)
8889
parser.add_argument("--flask-port", type=int, help="Port to listen on for monitoring interface")
8990
parser.add_argument("--flask-pass", type=int, help="Default password to Flask interface")
9091
parser.add_argument("--proxy-enable", action="store_true", help="Enable proxy after PyProxy")
@@ -94,63 +95,65 @@ def parse_args() -> argparse.Namespace:
9495
return parser.parse_args()
9596

9697

97-
def load_config(config_path: str) -> configparser.ConfigParser:
98+
def load_config(config_path: str) -> dict[str, Any]:
9899
"""
99-
Loads the configuration file and returns the parsed config object.
100+
Loads the YAML configuration file and returns it as a dictionary.
100101
101102
Args:
102-
config_path (str): The path to the configuration file to load.
103+
config_path (str): Path to config.yaml
103104
104105
Returns:
105-
configparser.ConfigParser: The parsed configuration object.
106+
dict[str, Any]: Parsed YAML configuration
106107
"""
107-
config = configparser.ConfigParser(interpolation=None)
108-
config.read(config_path)
108+
with open(config_path, "r", encoding="utf-8") as f:
109+
config = yaml.safe_load(f)
110+
if not isinstance(config, dict):
111+
raise ValueError("Invalid YAML configuration format.")
109112
return config
110113

111114

112115
def get_config_value(
113116
args: argparse.Namespace,
114-
config: configparser.ConfigParser,
117+
config: dict[str, Any],
115118
arg_name: str,
116119
section: str,
117120
fallback_value: str,
118-
) -> str:
121+
) -> Any:
119122
"""
120-
Retrieves the configuration value, either from the command-line
121-
arguments or from the config file.
123+
Retrieves the configuration value, either from CLI args, environment vars, or YAML config.s
122124
123125
Args:
124-
args (argparse.Namespace): The parsed command-line arguments object.
125-
config (configparser.ConfigParser): The parsed configuration object.
126-
arg_name (str): The name of the command-line argument.
127-
section (str): The section in the config file where the value is located.
128-
fallback_value (str): The fallback value to return if neither
129-
argument nor config has a value.
126+
args (argparse.Namespace): Parsed CLI args
127+
config (dict[str, Any]): YAML config
128+
arg_name (str): Argument name
129+
section (str): Section name (as top-level YAML key)s
130+
fallback_value (Any): Default value
130131
131132
Returns:
132-
str: The final value, either from command-line arguments, config file, or fallback.
133+
Any: The resulting value
133134
"""
134135
arg_value = getattr(args, arg_name, None)
135-
if arg_value:
136+
if arg_value not in (None, False, ""):
136137
return arg_value
137138

138139
env_var_name = f"PYPROXY_{arg_name.upper().replace('-', '_')}"
139140
env_value = os.getenv(env_var_name)
140141
if env_value:
141142
return env_value
142143

143-
return config.get(section, arg_name, fallback=fallback_value)
144+
section_lower = section.lower()
145+
if section_lower in config:
146+
section_data = config[section_lower]
147+
if isinstance(section_data, dict):
148+
return section_data.get(arg_name, fallback_value)
144149

150+
return fallback_value
145151

146-
def str_to_bool(value: str) -> bool:
147-
"""
148-
Converts a string representation of truth to a boolean value.
149152

150-
Args:
151-
value (str): The value to convert (e.g., "true", "1", "yes").
152-
153-
Returns:
154-
bool: True if the string represents a true value, False otherwise.
153+
def str_to_bool(value: Any) -> bool:
154+
"""
155+
Converts a string-like values into booleans
155156
"""
157+
if isinstance(value, bool):
158+
return value
156159
return str(value).lower() in ("yes", "true", "t", "1")

0 commit comments

Comments
 (0)