Skip to content

Commit

Permalink
feature: use dataclasses for config
Browse files Browse the repository at this point in the history
  • Loading branch information
aaronhnsy committed Jul 24, 2022
1 parent e45c9cc commit f564b38
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 90 deletions.
48 changes: 26 additions & 22 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,43 +1,47 @@
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
requires = ['poetry-core>=1.0.0']
build-backend = 'poetry.core.masonry.api'


[tool.poetry]
name = "Swish"
version = "0.0.1"
description = ""
name = 'Swish'
version = '0.0.1'
description = ''
authors = []


[tool.poetry.dependencies]
python = "^3.10"
aiohttp = "~3.8.0"
colorama = "~0.4.0"
toml = "~0.10.0"
typing_extensions = "~4.3.0"
yt-dlp = "~2022.7.0"
"discord.py" = { git = "https://github.com/Rapptz/discord.py" }
python = '^3.10'
aiohttp = '~3.8.0'
colorama = '~0.4.0'
toml = '~0.10.0'
typing_extensions = '~4.3.0'
yt-dlp = '~2022.7.0'
dacite = '~1.6.0'
'discord.py' = { git = 'https://github.com/Rapptz/discord.py' }

pyinstaller = { version = "*", optional = true }
jishaku = { version = "*", optional = true }
# 'build' extras
pyinstaller = { version = '*', optional = true }

# 'dev' extras
jishaku = { version = '*', optional = true }


[tool.poetry.extras]
build = ["pyinstaller"]
dev = ["jishaku"]
build = ['pyinstaller']
dev = ['jishaku']


[tool.pyright]
include = [
"swish",
# "tests"
]
pythonVersion = "3.10"
typeCheckingMode = "strict"
include = ['swish']
pythonVersion = '3.10'
typeCheckingMode = 'strict'
useLibraryCodeForTypes = true

reportUnknownMemberType = false
reportPrivateUsage = false
reportImportCycles = false
reportMissingTypeStubs = false
reportUnknownArgumentType = false
reportConstantRedefinition = false
reportPrivateImportUsage = false
25 changes: 13 additions & 12 deletions swish.toml
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
[SERVER]
host = '127.0.0.1'
[server]
host = "127.0.0.1"
port = 8000
password = 'helloworld!'
password = "helloworld!"

[IP]
[rotation]
enabled = false
method = "nanosecond-rotator"
blocks = []

[SEARCH]
[search]
max_results = 10

[LOGGING]
path = 'logs/'
[logging]
path = "logs/"
backup_count = 5
max_bytes = 5242880 # 5mb
max_bytes = 5242880

[LOGGING.LEVEL]
swish = 'DEBUG'
discord = 'NOTSET'
aiohttp = 'NOTSET'
[logging.levels]
swish = "DEBUG"
aiohttp = "NOTSET"
14 changes: 7 additions & 7 deletions swish/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ async def run(self) -> None:
)
await runner.setup()

host = CONFIG['SERVER']['host']
port = CONFIG['SERVER']['port']
host = CONFIG.server.host
port = CONFIG.server.port

site = aiohttp.web.TCPSite(
runner=runner,
Expand Down Expand Up @@ -101,7 +101,7 @@ async def websocket_handler(self, request: aiohttp.web.Request) -> aiohttp.web.W
await websocket.close(code=4000, message=b'Missing \'User-Id\' header.')
return websocket

password: str = CONFIG['SERVER']['password']
password: str = CONFIG.server.password
authorization: str | None = request.headers.get('Authorization')
if password != authorization:
LOG.error(f'{client_name} - Websocket connection failed due to mismatched \'Authorization\' header.')
Expand Down Expand Up @@ -181,10 +181,10 @@ def _decode_track_id(_id: str, /) -> dict[str, Any]:
}

_SOURCE_MAPPING: dict[str, str] = {
'youtube': f'ytsearch{CONFIG["SEARCH"]["max_results"]}:',
'soundcloud': f'scsearch{CONFIG["SEARCH"]["max_results"]}:',
'niconico': f'nicosearch{CONFIG["SEARCH"]["max_results"]}:',
'bilibili': f'bilisearch{CONFIG["SEARCH"]["max_results"]}:',
'youtube': f'ytsearch{CONFIG.search.max_results}:',
'soundcloud': f'scsearch{CONFIG.search.max_results}:',
'niconico': f'nicosearch{CONFIG.search.max_results}:',
'bilibili': f'bilisearch{CONFIG.search.max_results}:',
'none': ''
}

Expand Down
120 changes: 80 additions & 40 deletions swish/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,57 +15,97 @@
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from __future__ import annotations

import logging
from typing import Any
import dataclasses
import sys
from typing import Any, Literal

import dacite
import toml


LOG: logging.Logger = logging.getLogger('swish.config')


__all__ = (
'setup_config',
'CONFIG',
)

DEFAULT: dict[str, Any] = {
'server': {
'host': '127.0.0.1',
'port': 8000,
'password': 'helloworld!'
},
'rotation': {
'enabled': False,
'method': 'nanosecond-rotator',
'blocks': []
},
'search': {
'max_results': 10
},
'logging': {
'path': 'logs/',
'backup_count': 5,
'max_bytes': (2 ** 20) * 5,
'levels': {
'swish': 'DEBUG',
'aiohttp': 'NOTSET'
}
}
}

def setup_config() -> dict[str, Any]:

try:
config = toml.load('swish.toml')
LOG.info('Successfully loaded swish.toml configuration.')

except (toml.TomlDecodeError, FileNotFoundError, IOError):
config: dict[str, Any] = {
'SERVER': {
'host': '127.0.0.1',
'port': 8000,
'password': 'helloworld!'
},
'IP': {
'blocks': []
},
'SEARCH': {
'max_results': 10
},
'LOGGING': {
'path': 'logs/',
'backup_count': 5,
'max_bytes': 5242880,
'LEVEL': {
'swish': 'DEBUG',
'discord': 'NOTSET',
'aiohttp': 'NOTSET'
}
}

}
LOG.error('Could not find or load swish.toml, using default values.')
@dataclasses.dataclass
class Server:
host: str
port: int
password: str


@dataclasses.dataclass
class Rotation:
enabled: bool
method: Literal['nanosecond-rotator', 'ban-rotator']
blocks: list[str]


@dataclasses.dataclass
class Search:
max_results: int


@dataclasses.dataclass
class LoggingLevels:
swish: Literal['CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG', 'NOTSET']
aiohttp: Literal['CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG', 'NOTSET']


@dataclasses.dataclass
class Logging:
path: str
backup_count: int
max_bytes: int
levels: LoggingLevels


@dataclasses.dataclass
class Config:
server: Server
rotation: Rotation
search: Search
logging: Logging


try:
CONFIG: Config = dacite.from_dict(Config, toml.load('swish.toml'))

except (toml.TomlDecodeError, FileNotFoundError):

with open('swish.toml', 'w') as fp:
toml.dump(DEFAULT, fp)

return config
print('Could not find or parse swish.toml, using default configuration values.')
CONFIG: Config = dacite.from_dict(Config, DEFAULT)


CONFIG: dict[str, Any] = setup_config()
except dacite.DaciteError as error:
sys.exit(f'Your swish.toml configuration file is invalid: {str(error).capitalize()}.')
10 changes: 5 additions & 5 deletions swish/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,12 @@ def setup_logging() -> None:
'swish': logging.getLogger('swish'),
'aiohttp': logging.getLogger('aiohttp'),
}
loggers['swish'].setLevel(CONFIG['LOGGING']['LEVEL']['swish'])
loggers['aiohttp'].setLevel(CONFIG['LOGGING']['LEVEL']['aiohttp'])
loggers['swish'].setLevel(CONFIG.logging.levels.swish)
loggers['aiohttp'].setLevel(CONFIG.logging.levels.aiohttp)

for name, logger in loggers.items():

path = CONFIG['LOGGING']['path']
path = CONFIG.logging.path

if not os.path.exists(path):
os.makedirs(path)
Expand All @@ -85,8 +85,8 @@ def setup_logging() -> None:
file_handler = logging.handlers.RotatingFileHandler(
filename=f'{path}{name}.log',
mode='w',
maxBytes=CONFIG['LOGGING']['max_bytes'],
backupCount=CONFIG['LOGGING']['backup_count'],
maxBytes=CONFIG.logging.max_bytes,
backupCount=CONFIG.logging.backup_count,
encoding='utf-8',
)
file_handler.setFormatter(ColourFormatter(enabled=False))
Expand Down
7 changes: 3 additions & 4 deletions tests/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@
import aiohttp
import discord
import discord.types.voice
import toml
from discord.ext import commands


CONFIG: dict[str, Any] = toml.load('../swish.toml') # type: ignore
from swish.config import CONFIG


class Bot(commands.Bot):
Expand All @@ -37,9 +36,9 @@ async def on_ready(self) -> None:

self.session = aiohttp.ClientSession()
self.websocket = await self.session.ws_connect(
url=f'ws://{CONFIG["SERVER"]["host"]}:{CONFIG["SERVER"]["port"]}',
url=f'ws://{CONFIG.server.host}:{CONFIG.server.port}',
headers={
'Authorization': CONFIG['SERVER']['password'],
'Authorization': CONFIG.server.password,
'User-Agent': 'Python/v3.10.1,swish.py/v0.0.1a',
'User-Id': str(self.user.id),
},
Expand Down

0 comments on commit f564b38

Please sign in to comment.