diff --git a/.travis.yml b/.travis.yml index f8b72e2be..d9b4e6918 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,6 +31,7 @@ jobs: - TOXENV=py36 deploy: - provider: pypi + distributions: sdist bdist_wheel user: Red-DiscordBot password: secure: Ty9vYnd/wCuQkVC/OsS4E2jT9LVDVfzsFrQc4U2hMYcTJnYbl/3omyObdCWCOBC40vUDkVHAQU8ULHzoCA+2KX9Ds/7/P5zCumAA0uJRR9Smw7OlRzSMxJI+/lGq4CwXKzxDZKuo5rsxXEbW5qmYjtO8Mk6KuLkvieb1vyr2DcqWEFzg/7TZNDfD1oP8et8ITQ26lLP1dtQx/jlAiIBzgK9wziuwj1Divb9A///VsGz43N8maZ+jfsDjYqrfUVWTy3ar7JPUplletenYCR1PmQ5C46XfV0kitKd1aITJ48YPAKyYgKy8AIT+Uz1JArTnqdzLSFRNELS57qS00lzgllbteCyWQ8Uzy0Zpxb/5DDH8/mL1n0MyJrF8qjZd2hLNAXg3z/k9bGXeiMLGwoxRlGXkL2XpiVgI93UKKyVyooGNMgPTc/QdSc7krjAWcOtX/HgLR34jxeLPFEdzJNAFIimfDD8N+XTFcNBw6EvOYm/n5MXkckNoX/G+ThNobHZ7VKSASltZ9zBRAJ2dDh35G3CYmVEk33U77RKbL9le/Za9QVBcAO8i6rqVGYkdO7thHHKHc/1CB1jNnjsFSDt0bURtNfAqfwKCurQC8487zbEzT+2fog3Wygv7g3cklaRg4guY8UjZuFWStYGqbroTsOCd9ATNqeO5B13pNhllSzU= diff --git a/redbot/__init__.py b/redbot/__init__.py index f080307c0..fd3a01fa4 100644 --- a/redbot/__init__.py +++ b/redbot/__init__.py @@ -1,29 +1,161 @@ -import sys -import warnings -import discord -import colorama +import re as _re +import sys as _sys +from math import inf as _inf +from typing import ( + Any as _Any, + ClassVar as _ClassVar, + Dict as _Dict, + List as _List, + Optional as _Optional, + Pattern as _Pattern, + Tuple as _Tuple, + Union as _Union, +) + MIN_PYTHON_VERSION = (3, 7, 0) -if sys.version_info < MIN_PYTHON_VERSION: +__all__ = ["MIN_PYTHON_VERSION", "__version__", "version_info", "VersionInfo"] + +if _sys.version_info < MIN_PYTHON_VERSION: print( f"Python {'.'.join(map(str, MIN_PYTHON_VERSION))} is required to run Red, but you have " - f"{sys.version}! Please update Python." + f"{_sys.version}! Please update Python." ) - sys.exit(1) + _sys.exit(1) -if discord.version_info.major < 1: - print( - "You are not running the rewritten version of discord.py.\n\n" - "In order to use Red V3 you MUST be running d.py version " - "1.0.0 or greater." + +class VersionInfo: + ALPHA = "alpha" + BETA = "beta" + RELEASE_CANDIDATE = "release candidate" + FINAL = "final" + + _VERSION_STR_PATTERN: _ClassVar[_Pattern[str]] = _re.compile( + r"^" + r"(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)" + r"(?:(?Pa|b|rc)(?P0|[1-9]\d*))?" + r"(?:\.post(?P0|[1-9]\d*))?" + r"(?:\.dev(?P0|[1-9]\d*))?" + r"$", + flags=_re.IGNORECASE, ) - sys.exit(1) + _RELEASE_LEVELS: _ClassVar[_List[str]] = [ALPHA, BETA, RELEASE_CANDIDATE, FINAL] + _SHORT_RELEASE_LEVELS: _ClassVar[_Dict[str, str]] = { + "a": ALPHA, + "b": BETA, + "rc": RELEASE_CANDIDATE, + } + + def __init__( + self, + major: int, + minor: int, + micro: int, + releaselevel: str, + serial: _Optional[int] = None, + post_release: _Optional[int] = None, + dev_release: _Optional[int] = None, + ) -> None: + self.major: int = major + self.minor: int = minor + self.micro: int = micro + + if releaselevel not in self._RELEASE_LEVELS: + raise TypeError(f"'releaselevel' must be one of: {', '.join(self._RELEASE_LEVELS)}") + + self.releaselevel: str = releaselevel + self.serial: _Optional[int] = serial + self.post_release: _Optional[int] = post_release + self.dev_release: _Optional[int] = dev_release + + @classmethod + def from_str(cls, version_str: str) -> "VersionInfo": + """Parse a string into a VersionInfo object. + + Raises + ------ + ValueError + If the version info string is invalid. + + """ + match = cls._VERSION_STR_PATTERN.match(version_str) + if not match: + raise ValueError(f"Invalid version string: {version_str}") + + kwargs: _Dict[str, _Union[str, int]] = {} + for key in ("major", "minor", "micro"): + kwargs[key] = int(match[key]) + releaselevel = match["releaselevel"] + if releaselevel is not None: + kwargs["releaselevel"] = cls._SHORT_RELEASE_LEVELS[releaselevel] + else: + kwargs["releaselevel"] = cls.FINAL + for key in ("serial", "post_release", "dev_release"): + if match[key] is not None: + kwargs[key] = int(match[key]) + return cls(**kwargs) + + @classmethod + def from_json( + cls, data: _Union[_Dict[str, _Union[int, str]], _List[_Union[int, str]]] + ) -> "VersionInfo": + if isinstance(data, _List): + # For old versions, data was stored as a list: + # [MAJOR, MINOR, MICRO, RELEASELEVEL, SERIAL] + return cls(*data) + else: + return cls(**data) + + def to_json(self) -> _Dict[str, _Union[int, str]]: + return { + "major": self.major, + "minor": self.minor, + "micro": self.micro, + "releaselevel": self.releaselevel, + "serial": self.serial, + "post_release": self.post_release, + "dev_release": self.dev_release, + } + + def __lt__(self, other: _Any) -> bool: + if not isinstance(other, VersionInfo): + return NotImplemented + tups: _List[_Tuple[int, int, int, int, int, int, int]] = [] + for obj in (self, other): + tups.append( + ( + obj.major, + obj.minor, + obj.micro, + obj._RELEASE_LEVELS.index(obj.releaselevel), + obj.serial if obj.serial is not None else _inf, + obj.post_release if obj.post_release is not None else -_inf, + obj.dev_release if obj.dev_release is not None else _inf, + ) + ) + return tups[0] < tups[1] + + def __str__(self) -> str: + ret = f"{self.major}.{self.minor}.{self.micro}" + if self.releaselevel != self.FINAL: + short = next( + k for k, v in self._SHORT_RELEASE_LEVELS.items() if v == self.releaselevel + ) + ret += f"{short}{self.serial}" + if self.post_release is not None: + ret += f".post{self.post_release}" + if self.dev_release is not None: + ret += f".dev{self.dev_release}" + return ret + + def __repr__(self) -> str: + return ( + "VersionInfo(major={major}, minor={minor}, micro={micro}, " + "releaselevel={releaselevel}, serial={serial}, post={post_release}, " + "dev={dev_release})".format(**self.to_json()) + ) -colorama.init() - -# Filter fuzzywuzzy slow sequence matcher warning -warnings.filterwarnings("ignore", module=r"fuzzywuzzy.*") -# Prevent discord PyNaCl missing warning -discord.voice_client.VoiceClient.warn_nacl = False +__version__ = "3.0.0" +version_info = VersionInfo.from_str(__version__) diff --git a/redbot/core/__init__.py b/redbot/core/__init__.py index 9876bd032..426eca53e 100644 --- a/redbot/core/__init__.py +++ b/redbot/core/__init__.py @@ -1,152 +1,12 @@ -import re as _re -from math import inf as _inf -from typing import ( - Any as _Any, - ClassVar as _ClassVar, - Dict as _Dict, - List as _List, - Optional as _Optional, - Pattern as _Pattern, - Tuple as _Tuple, - Union as _Union, -) +import colorama as _colorama +import discord as _discord +from .. import __version__, version_info, VersionInfo from .config import Config __all__ = ["Config", "__version__", "version_info", "VersionInfo"] +_colorama.init() -class VersionInfo: - ALPHA = "alpha" - BETA = "beta" - RELEASE_CANDIDATE = "release candidate" - FINAL = "final" - - _VERSION_STR_PATTERN: _ClassVar[_Pattern[str]] = _re.compile( - r"^" - r"(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)" - r"(?:(?Pa|b|rc)(?P0|[1-9]\d*))?" - r"(?:\.post(?P0|[1-9]\d*))?" - r"(?:\.dev(?P0|[1-9]\d*))?" - r"$", - flags=_re.IGNORECASE, - ) - _RELEASE_LEVELS: _ClassVar[_List[str]] = [ALPHA, BETA, RELEASE_CANDIDATE, FINAL] - _SHORT_RELEASE_LEVELS: _ClassVar[_Dict[str, str]] = { - "a": ALPHA, - "b": BETA, - "rc": RELEASE_CANDIDATE, - } - - def __init__( - self, - major: int, - minor: int, - micro: int, - releaselevel: str, - serial: _Optional[int] = None, - post_release: _Optional[int] = None, - dev_release: _Optional[int] = None, - ) -> None: - self.major: int = major - self.minor: int = minor - self.micro: int = micro - - if releaselevel not in self._RELEASE_LEVELS: - raise TypeError(f"'releaselevel' must be one of: {', '.join(self._RELEASE_LEVELS)}") - - self.releaselevel: str = releaselevel - self.serial: _Optional[int] = serial - self.post_release: _Optional[int] = post_release - self.dev_release: _Optional[int] = dev_release - - @classmethod - def from_str(cls, version_str: str) -> "VersionInfo": - """Parse a string into a VersionInfo object. - - Raises - ------ - ValueError - If the version info string is invalid. - - """ - match = cls._VERSION_STR_PATTERN.match(version_str) - if not match: - raise ValueError(f"Invalid version string: {version_str}") - - kwargs: _Dict[str, _Union[str, int]] = {} - for key in ("major", "minor", "micro"): - kwargs[key] = int(match[key]) - releaselevel = match["releaselevel"] - if releaselevel is not None: - kwargs["releaselevel"] = cls._SHORT_RELEASE_LEVELS[releaselevel] - else: - kwargs["releaselevel"] = cls.FINAL - for key in ("serial", "post_release", "dev_release"): - if match[key] is not None: - kwargs[key] = int(match[key]) - return cls(**kwargs) - - @classmethod - def from_json( - cls, data: _Union[_Dict[str, _Union[int, str]], _List[_Union[int, str]]] - ) -> "VersionInfo": - if isinstance(data, _List): - # For old versions, data was stored as a list: - # [MAJOR, MINOR, MICRO, RELEASELEVEL, SERIAL] - return cls(*data) - else: - return cls(**data) - - def to_json(self) -> _Dict[str, _Union[int, str]]: - return { - "major": self.major, - "minor": self.minor, - "micro": self.micro, - "releaselevel": self.releaselevel, - "serial": self.serial, - "post_release": self.post_release, - "dev_release": self.dev_release, - } - - def __lt__(self, other: _Any) -> bool: - if not isinstance(other, VersionInfo): - return NotImplemented - tups: _List[_Tuple[int, int, int, int, int, int, int]] = [] - for obj in (self, other): - tups.append( - ( - obj.major, - obj.minor, - obj.micro, - obj._RELEASE_LEVELS.index(obj.releaselevel), - obj.serial if obj.serial is not None else _inf, - obj.post_release if obj.post_release is not None else -_inf, - obj.dev_release if obj.dev_release is not None else _inf, - ) - ) - return tups[0] < tups[1] - - def __str__(self) -> str: - ret = f"{self.major}.{self.minor}.{self.micro}" - if self.releaselevel != self.FINAL: - short = next( - k for k, v in self._SHORT_RELEASE_LEVELS.items() if v == self.releaselevel - ) - ret += f"{short}{self.serial}" - if self.post_release is not None: - ret += f".post{self.post_release}" - if self.dev_release is not None: - ret += f".dev{self.dev_release}" - return ret - - def __repr__(self) -> str: - return ( - "VersionInfo(major={major}, minor={minor}, micro={micro}, " - "releaselevel={releaselevel}, serial={serial}, post={post_release}, " - "dev={dev_release})".format(**self.to_json()) - ) - - -__version__ = "3.0.0" -version_info = VersionInfo.from_str(__version__) +# Prevent discord PyNaCl missing warning +_discord.voice_client.VoiceClient.warn_nacl = False diff --git a/setup.cfg b/setup.cfg index e9e665059..d9c79d91b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,3 +1,110 @@ [metadata] +name = Red-DiscordBot +version = attr: redbot.__version__ +description = A highly customisable Discord bot +license = GPL-3.0 long_description = file: README.md long_description_content_type = text/markdown; charset=UTF-8; variant=GFM +author = Cog-Creators +author_email = cogcreators@gmail.com +url = https://github.com/Cog-Creators/Red-DiscordBot +classifiers = + # List at https://pypi.org/pypi?%3Aaction=list_classifiers + Development Status :: 5 - Production/Stable + Framework :: AsyncIO + Framework :: Pytest + Intended Audience :: Developers + Intended Audience :: End Users/Desktop + License :: OSI Approved :: GNU General Public License v3 (GPLv3) + Natural Language :: English + Operating System :: OS Independent + Programming Language :: Python :: 3.7 + Topic :: Communications :: Chat + Topic :: Documentation :: Sphinx + +[options] +packages = find_namespace: +python_requires = >=3.7 +install_requires = + aiohttp-json-rpc==0.11.2 + aiohttp==3.4.4 + appdirs==1.4.3 + async-timeout==3.0.1 + attrs==18.2.0 + chardet==3.0.4 + colorama==0.4.1 + distro==1.3.0; sys_platform == "linux" + fuzzywuzzy==0.17.0 + idna-ssl==1.1.0 + idna==2.8 + multidict==4.5.2 + python-levenshtein-wheels==0.13.1 + pyyaml==3.13 + raven==6.10.0 + raven-aiohttp==0.7.0 + red-lavalink==0.2.0 + schema==0.6.8 + websockets==6.0 + yarl==1.3.0 + +[options.extras_require] +docs = + alabaster==0.7.12 + babel==2.6.0 + certifi==2018.11.29 + docutils==0.14 + imagesize==1.1.0 + Jinja2==2.10 + MarkupSafe==1.1.0 + packaging==18.0 + pyparsing==2.3.0 + Pygments==2.3.1 + pytz==2018.9 + requests==2.21.0 + six==1.12.0 + snowballstemmer==1.2.1 + sphinx==1.8.3 + sphinx_rtd_theme==0.4.2 + sphinxcontrib-asyncio==0.2.0 + sphinxcontrib-websupport==1.1.0 + urllib3==1.24.1 +mongo = + motor==2.0.0 + pymongo==3.7.2 + dnspython==1.16.0 +style = + black==18.9b0 + click==7.0 + toml==0.10.0 +test = + atomicwrites==1.2.1 + more-itertools==5.0.0 + pluggy==0.8.1 + py==1.7.0 + pytest==4.1.0 + pytest-asyncio==0.10.0 + six==1.12.0 + +[options.entry_points] +console_scripts = + redbot=redbot.__main__:main + redbot-setup=redbot.setup:main + redbot-launcher=redbot.launcher:main +pytest11 = + red-discordbot=redbot.pytest + +[options.packages.find] +include = + redbot + redbot.* + discord + discord.ext.commands + +[options.package_data] +* = + locales/*.po + **/locales/*.po + data/* + data/**/* +discord = + bin/*.dll diff --git a/setup.py b/setup.py index e05fdb45a..9522f43b7 100644 --- a/setup.py +++ b/setup.py @@ -1,110 +1,4 @@ -import os -import re -from setuptools import setup, find_namespace_packages +from setuptools import setup -install_requires = [ - "aiohttp-json-rpc==0.11.2", - "aiohttp==3.4.4", - "appdirs==1.4.3", - "async-timeout==3.0.1", - "attrs==18.2.0", - "chardet==3.0.4", - "colorama==0.4.1", - "distro==1.3.0; sys_platform == 'linux'", - "fuzzywuzzy==0.17.0", - "idna-ssl==1.1.0", - "idna==2.8", - "multidict==4.5.2", - "python-levenshtein-wheels==0.13.1", - "pyyaml==3.13", - "raven==6.10.0", - "raven-aiohttp==0.7.0", - "red-lavalink==0.2.0", - "schema==0.6.8", - "websockets==6.0", - "yarl==1.3.0", -] - -extras_require = { - "test": [ - "atomicwrites==1.2.1", - "more-itertools==5.0.0", - "pluggy==0.8.1", - "py==1.7.0", - "pytest==4.1.0", - "pytest-asyncio==0.10.0", - "six==1.12.0", - ], - "mongo": ["motor==2.0.0", "pymongo==3.7.2", "dnspython==1.16.0"], - "docs": [ - "alabaster==0.7.12", - "babel==2.6.0", - "certifi==2018.11.29", - "docutils==0.14", - "imagesize==1.1.0", - "Jinja2==2.10", - "MarkupSafe==1.1.0", - "packaging==18.0", - "pyparsing==2.3.0", - "Pygments==2.3.1", - "pytz==2018.9", - "requests==2.21.0", - "six==1.12.0", - "snowballstemmer==1.2.1", - "sphinx==1.8.3", - "sphinx_rtd_theme==0.4.2", - "sphinxcontrib-asyncio==0.2.0", - "sphinxcontrib-websupport==1.1.0", - "urllib3==1.24.1", - ], - "voice": [], - "style": ["black==18.9b0", "click==7.0", "toml==0.10.0"], -} - -python_requires = ">=3.7,<3.8" - - -def get_version(): - with open("redbot/core/__init__.py") as f: - version = re.search( - r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', f.read(), re.MULTILINE - ).group(1) - return version - - -if __name__ == "__main__": - setup( - name="Red-DiscordBot", - version=get_version(), - packages=find_namespace_packages( - include=["redbot", "redbot.*", "discord", "discord.ext.commands"] - ), - package_data={"": ["locales/*.po", "data/*", "data/**/*"]}, - url="https://github.com/Cog-Creators/Red-DiscordBot", - license="GPLv3", - author="Cog-Creators", - author_email="", - description="A highly customizable Discord bot", - classifiers=[ - "Development Status :: 4 - Beta", - "Framework :: AsyncIO", - "Framework :: Pytest", - "Intended Audience :: Developers", - "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)", - "Operating System :: OS Independent", - "Programming Language :: Python :: 3.7", - "Topic :: Communications :: Chat", - "Topic :: Documentation :: Sphinx", - ], - entry_points={ - "console_scripts": [ - "redbot=redbot.__main__:main", - "redbot-setup=redbot.setup:main", - "redbot-launcher=redbot.launcher:main", - ], - "pytest11": ["red-discordbot = redbot.pytest"], - }, - python_requires=python_requires, - install_requires=install_requires, - extras_require=extras_require, - ) +# Metadata and options defined in setup.cfg +setup()