From 9fd7b0829162bb589b371215e5772b24d2bd7d38 Mon Sep 17 00:00:00 2001 From: RohanJnr Date: Sat, 29 Feb 2020 14:29:42 +0530 Subject: Added all the tag files in resources and modified cogs/tags.py file to access the static tag files rather than sending an API get request. Removed all methods calling the API so the tags cannot be edited, added nor deleted. --- bot/cogs/tags.py | 103 +++++----------------------- bot/resources/tags/args-kwargs.md | 17 +++++ bot/resources/tags/ask.md | 9 +++ bot/resources/tags/class.md | 25 +++++++ bot/resources/tags/classmethod.md | 20 ++++++ bot/resources/tags/codeblock.md | 17 +++++ bot/resources/tags/decorators.md | 31 +++++++++ bot/resources/tags/dictcomps.md | 20 ++++++ bot/resources/tags/enumerate.md | 13 ++++ bot/resources/tags/except.md | 17 +++++ bot/resources/tags/exit().md | 8 +++ bot/resources/tags/f-strings.md | 17 +++++ bot/resources/tags/foo.md | 10 +++ bot/resources/tags/functions-are-objects.md | 39 +++++++++++ bot/resources/tags/global.md | 16 +++++ bot/resources/tags/if-name-main.md | 26 +++++++ bot/resources/tags/indent.md | 24 +++++++ bot/resources/tags/inline.md | 16 +++++ bot/resources/tags/iterate-dict.md | 10 +++ bot/resources/tags/listcomps.md | 14 ++++ bot/resources/tags/mutable-default-args.md | 48 +++++++++++++ bot/resources/tags/names.md | 37 ++++++++++ bot/resources/tags/off-topic.md | 8 +++ bot/resources/tags/open.md | 26 +++++++ bot/resources/tags/or-gotcha.md | 17 +++++ bot/resources/tags/param-arg.md | 12 ++++ bot/resources/tags/paste.md | 6 ++ bot/resources/tags/pathlib.md | 21 ++++++ bot/resources/tags/pep8.md | 3 + bot/resources/tags/positional-keyword.md | 38 ++++++++++ bot/resources/tags/precedence.md | 13 ++++ bot/resources/tags/quotes.md | 20 ++++++ bot/resources/tags/relative-path.md | 7 ++ bot/resources/tags/repl.md | 13 ++++ bot/resources/tags/return.md | 35 ++++++++++ bot/resources/tags/round.md | 24 +++++++ bot/resources/tags/scope.md | 24 +++++++ bot/resources/tags/seek.md | 22 ++++++ bot/resources/tags/self.md | 25 +++++++ bot/resources/tags/star-imports.md | 48 +++++++++++++ bot/resources/tags/traceback.md | 18 +++++ bot/resources/tags/windows-path.md | 30 ++++++++ bot/resources/tags/with.md | 8 +++ bot/resources/tags/xy-problem.md | 7 ++ bot/resources/tags/ytdl.md | 9 +++ bot/resources/tags/zen.md | 20 ++++++ bot/resources/tags/zip.md | 12 ++++ 47 files changed, 919 insertions(+), 84 deletions(-) create mode 100644 bot/resources/tags/args-kwargs.md create mode 100644 bot/resources/tags/ask.md create mode 100644 bot/resources/tags/class.md create mode 100644 bot/resources/tags/classmethod.md create mode 100644 bot/resources/tags/codeblock.md create mode 100644 bot/resources/tags/decorators.md create mode 100644 bot/resources/tags/dictcomps.md create mode 100644 bot/resources/tags/enumerate.md create mode 100644 bot/resources/tags/except.md create mode 100644 bot/resources/tags/exit().md create mode 100644 bot/resources/tags/f-strings.md create mode 100644 bot/resources/tags/foo.md create mode 100644 bot/resources/tags/functions-are-objects.md create mode 100644 bot/resources/tags/global.md create mode 100644 bot/resources/tags/if-name-main.md create mode 100644 bot/resources/tags/indent.md create mode 100644 bot/resources/tags/inline.md create mode 100644 bot/resources/tags/iterate-dict.md create mode 100644 bot/resources/tags/listcomps.md create mode 100644 bot/resources/tags/mutable-default-args.md create mode 100644 bot/resources/tags/names.md create mode 100644 bot/resources/tags/off-topic.md create mode 100644 bot/resources/tags/open.md create mode 100644 bot/resources/tags/or-gotcha.md create mode 100644 bot/resources/tags/param-arg.md create mode 100644 bot/resources/tags/paste.md create mode 100644 bot/resources/tags/pathlib.md create mode 100644 bot/resources/tags/pep8.md create mode 100644 bot/resources/tags/positional-keyword.md create mode 100644 bot/resources/tags/precedence.md create mode 100644 bot/resources/tags/quotes.md create mode 100644 bot/resources/tags/relative-path.md create mode 100644 bot/resources/tags/repl.md create mode 100644 bot/resources/tags/return.md create mode 100644 bot/resources/tags/round.md create mode 100644 bot/resources/tags/scope.md create mode 100644 bot/resources/tags/seek.md create mode 100644 bot/resources/tags/self.md create mode 100644 bot/resources/tags/star-imports.md create mode 100644 bot/resources/tags/traceback.md create mode 100644 bot/resources/tags/windows-path.md create mode 100644 bot/resources/tags/with.md create mode 100644 bot/resources/tags/xy-problem.md create mode 100644 bot/resources/tags/ytdl.md create mode 100644 bot/resources/tags/zen.md create mode 100644 bot/resources/tags/zip.md diff --git a/bot/cogs/tags.py b/bot/cogs/tags.py index b6360dfae..0e959b45f 100644 --- a/bot/cogs/tags.py +++ b/bot/cogs/tags.py @@ -1,22 +1,22 @@ import logging +import os import re import time +from pathlib import Path from typing import Dict, List, Optional from discord import Colour, Embed from discord.ext.commands import Cog, Context, group from bot.bot import Bot -from bot.constants import Channels, Cooldowns, MODERATION_ROLES, Roles -from bot.converters import TagContentConverter, TagNameConverter -from bot.decorators import with_role +from bot.constants import Channels, Cooldowns +from bot.converters import TagNameConverter from bot.pagination import LinePaginator log = logging.getLogger(__name__) TEST_CHANNELS = ( - Channels.devtest, - Channels.bot, + Channels.bot_commands, Channels.helpers ) @@ -29,7 +29,6 @@ class Tags(Cog): def __init__(self, bot: Bot): self.bot = bot self.tag_cooldowns = {} - self._cache = {} self._last_fetch: float = 0.0 @@ -38,12 +37,23 @@ class Tags(Cog): # refresh only when there's a more than 5m gap from last call. time_now: float = time.time() if is_forced or not self._last_fetch or time_now - self._last_fetch > 5 * 60: - tags = await self.bot.api_client.get('bot/tags') - self._cache = {tag['title'].lower(): tag for tag in tags} + tag_files = os.listdir("bot/resources/tags") + for file in tag_files: + p = Path("bot", "resources", "tags", file) + tag_title = os.path.splitext(file)[0].lower() + with p.open() as f: + tag = { + "title": tag_title, + "embed": { + "description": f.read() + } + } + self._cache[tag_title] = tag + self._last_fetch = time_now @staticmethod - def _fuzzy_search(search: str, target: str) -> int: + def _fuzzy_search(search: str, target: str) -> float: """A simple scoring algorithm based on how many letters are found / total, with order in mind.""" current, index = 0, 0 _search = REGEX_NON_ALPHABET.sub('', search.lower()) @@ -159,81 +169,6 @@ class Tags(Cog): max_lines=15 ) - @tags_group.command(name='set', aliases=('add', 's')) - @with_role(*MODERATION_ROLES) - async def set_command( - self, - ctx: Context, - tag_name: TagNameConverter, - *, - tag_content: TagContentConverter, - ) -> None: - """Create a new tag.""" - body = { - 'title': tag_name.lower().strip(), - 'embed': { - 'title': tag_name, - 'description': tag_content - } - } - - await self.bot.api_client.post('bot/tags', json=body) - self._cache[tag_name.lower()] = await self.bot.api_client.get(f'bot/tags/{tag_name}') - - log.debug(f"{ctx.author} successfully added the following tag to our database: \n" - f"tag_name: {tag_name}\n" - f"tag_content: '{tag_content}'\n") - - await ctx.send(embed=Embed( - title="Tag successfully added", - description=f"**{tag_name}** added to tag database.", - colour=Colour.blurple() - )) - - @tags_group.command(name='edit', aliases=('e', )) - @with_role(*MODERATION_ROLES) - async def edit_command( - self, - ctx: Context, - tag_name: TagNameConverter, - *, - tag_content: TagContentConverter, - ) -> None: - """Edit an existing tag.""" - body = { - 'embed': { - 'title': tag_name, - 'description': tag_content - } - } - - await self.bot.api_client.patch(f'bot/tags/{tag_name}', json=body) - self._cache[tag_name.lower()] = await self.bot.api_client.get(f'bot/tags/{tag_name}') - - log.debug(f"{ctx.author} successfully edited the following tag in our database: \n" - f"tag_name: {tag_name}\n" - f"tag_content: '{tag_content}'\n") - - await ctx.send(embed=Embed( - title="Tag successfully edited", - description=f"**{tag_name}** edited in the database.", - colour=Colour.blurple() - )) - - @tags_group.command(name='delete', aliases=('remove', 'rm', 'd')) - @with_role(Roles.admin, Roles.owner) - async def delete_command(self, ctx: Context, *, tag_name: TagNameConverter) -> None: - """Remove a tag from the database.""" - await self.bot.api_client.delete(f'bot/tags/{tag_name}') - self._cache.pop(tag_name.lower(), None) - - log.debug(f"{ctx.author} successfully deleted the tag called '{tag_name}'") - await ctx.send(embed=Embed( - title=tag_name, - description=f"Tag successfully removed: {tag_name}.", - colour=Colour.blurple() - )) - def setup(bot: Bot) -> None: """Load the Tags cog.""" diff --git a/bot/resources/tags/args-kwargs.md b/bot/resources/tags/args-kwargs.md new file mode 100644 index 000000000..fb19d39fd --- /dev/null +++ b/bot/resources/tags/args-kwargs.md @@ -0,0 +1,17 @@ +`*args` and `**kwargs` + +These special parameters allow functions to take arbitrary amounts of positional and keyword arguments. The names `args` and `kwargs` are purely convention, and could be named any other valid variable name. The special functionality comes from the single and double asterisks (`*`). If both are used in a function signature, `*args` **must** appear before `**kwargs`. + +**Single asterisk** +`*args` will ingest an arbitrary amount of **positional arguments**, and store it in a tuple. If there are parameters after `*args` in the parameter list with no default value, they will become **required** keyword arguments by default. + +**Double asterisk** +`**kwargs` will ingest an arbitrary amount of **keyword arguments**, and store it in a dictionary. There can be **no** additional parameters **after** `**kwargs` in the parameter list. + +**Use cases** +• **Decorators** (see `!tags decorators`) +• **Inheritance** (overriding methods) +• **Future proofing** (in the case of the first two bullet points, if the parameters change, your code won't break) +• **Flexibility** (writing functions that behave like `dict()` or `print()`) + +*See* `!tags positional-keyword` *for information about positional and keyword arguments* \ No newline at end of file diff --git a/bot/resources/tags/ask.md b/bot/resources/tags/ask.md new file mode 100644 index 000000000..07f9bd84d --- /dev/null +++ b/bot/resources/tags/ask.md @@ -0,0 +1,9 @@ +Asking good questions will yield a much higher chance of a quick response: + +• Don't ask to ask your question, just go ahead and tell us your problem. +• Don't ask if anyone is knowledgeable in some area, filtering serves no purpose. +• Try to solve the problem on your own first, we're not going to write code for you. +• Show us the code you've tried and any errors or unexpected results it's giving. +• Be patient while we're helping you. + +You can find a much more detailed explanation [on our website](https://pythondiscord.com/pages/asking-good-questions/). \ No newline at end of file diff --git a/bot/resources/tags/class.md b/bot/resources/tags/class.md new file mode 100644 index 000000000..74c36b9fa --- /dev/null +++ b/bot/resources/tags/class.md @@ -0,0 +1,25 @@ +**Classes** + +Classes are used to create objects that have specific behavior. + +Every object in python has a class, including `list`s, `dict`ionaries and even numbers. Using a class to group code and data like this is the foundation of Object Oriented Programming. Classes allow you to expose a simple, consistent interface while hiding the more complicated details. This simplifies the rest of your program and makes it easier to separately maintain and debug each component. + +Here is an example class: + +```python +class Foo: + def __init__(self, somedata): + self.my_attrib = somedata + + def show(self): + print(self.my_attrib) +``` + +To use a class, you need to instantiate it. The following creates a new object named `bar`, with `Foo` as its class. + +```python +bar = Foo('data') +bar.show() +``` + +We can access any of `Foo`'s methods via `bar.my_method()`, and access any of `bar`s data via `bar.my_attribute`. \ No newline at end of file diff --git a/bot/resources/tags/classmethod.md b/bot/resources/tags/classmethod.md new file mode 100644 index 000000000..43c6d9909 --- /dev/null +++ b/bot/resources/tags/classmethod.md @@ -0,0 +1,20 @@ +Although most methods are tied to an _object instance_, it can sometimes be useful to create a method that does something with _the class itself_. To achieve this in Python, you can use the `@classmethod` decorator. This is often used to provide alternative constructors for a class. + +For example, you may be writing a class that takes some magic token (like an API key) as a constructor argument, but you sometimes read this token from a configuration file. You could make use of a `@classmethod` to create an alternate constructor for when you want to read from the configuration file. +```py +class Bot: + def __init__(self, token: str): + self._token = token + + @classmethod + def from_config(cls, config: dict) -> Bot: + token = config['token'] + return cls(token) + +# now we can create the bot instance like this +alternative_bot = Bot.from_config(default_config) + +# but this still works, too +regular_bot = Bot("tokenstring") +``` +This is just one of the many use cases of `@classmethod`. A more in-depth explanation can be found [here](https://stackoverflow.com/questions/12179271/meaning-of-classmethod-and-staticmethod-for-beginner#12179752). \ No newline at end of file diff --git a/bot/resources/tags/codeblock.md b/bot/resources/tags/codeblock.md new file mode 100644 index 000000000..816bb8232 --- /dev/null +++ b/bot/resources/tags/codeblock.md @@ -0,0 +1,17 @@ +Discord has support for Markdown, which allows you to post code with full syntax highlighting. Please use these whenever you paste code, as this helps improve the legibility and makes it easier for us to help you. + +To do this, use the following method: + +\```python +print('Hello world!') +\``` + +Note: +• **These are backticks, not quotes.** Backticks can usually be found on the tilde key. +• You can also use py as the language instead of python +• The language must be on the first line next to the backticks with **no** space between them + +This will result in the following: +```py +print('Hello world!') +``` \ No newline at end of file diff --git a/bot/resources/tags/decorators.md b/bot/resources/tags/decorators.md new file mode 100644 index 000000000..3ff1db16c --- /dev/null +++ b/bot/resources/tags/decorators.md @@ -0,0 +1,31 @@ +**Decorators** + +A decorator is a function that modifies another function. + +Consider the following example of a timer decorator: +```py +>>> import time +>>> def timer(f): +... def inner(*args, **kwargs): +... start = time.time() +... result = f(*args, **kwargs) +... print('Time elapsed:', time.time() - start) +... return result +... return inner +... +>>> @timer +... def slow(delay=1): +... time.sleep(delay) +... return 'Finished!' +... +>>> print(slow()) +Time elapsed: 1.0011568069458008 +Finished! +>>> print(slow(3)) +Time elapsed: 3.000307321548462 +Finished! +``` + +More information: +• [Corey Schafer's video on decorators](https://youtu.be/FsAPt_9Bf3U) +• [Real python article](https://realpython.com/primer-on-python-decorators/) \ No newline at end of file diff --git a/bot/resources/tags/dictcomps.md b/bot/resources/tags/dictcomps.md new file mode 100644 index 000000000..ddefa1299 --- /dev/null +++ b/bot/resources/tags/dictcomps.md @@ -0,0 +1,20 @@ +**Dictionary Comprehensions** + +Like lists, there is a convenient way of creating dictionaries: +```py +>>> ftoc = {f: round((5/9)*(f-32)) for f in range(-40,101,20)} +>>> print(ftoc) +{-40: -40, -20: -29, 0: -18, 20: -7, 40: 4, 60: 16, 80: 27, 100: 38} +``` +In the example above, I created a dictionary of temperatures in Fahrenheit, that are mapped to (*roughly*) their Celsius counterpart within a small range. These comprehensions are useful for succinctly creating dictionaries from some other sequence. + +They are also very useful for inverting the key value pairs of a dictionary that already exists, such that the value in the old dictionary is now the key, and the corresponding key is now its value: +```py +>>> ctof = {v:k for k, v in ftoc.items()} +>>> print(ctof) +{-40: -40, -29: -20, -18: 0, -7: 20, 4: 40, 16: 60, 27: 80, 38: 100} +``` + +Also like list comprehensions, you can add a conditional to it in order to filter out items you don't want. + +For more information and examples, check [PEP 274](https://www.python.org/dev/peps/pep-0274/) \ No newline at end of file diff --git a/bot/resources/tags/enumerate.md b/bot/resources/tags/enumerate.md new file mode 100644 index 000000000..610843cf4 --- /dev/null +++ b/bot/resources/tags/enumerate.md @@ -0,0 +1,13 @@ +Ever find yourself in need of the current iteration number of your `for` loop? You should use **enumerate**! Using `enumerate`, you can turn code that looks like this: +```py +index = 0 +for item in my_list: + print(f"{index}: {item}") + index += 1 +``` +into beautiful, _pythonic_ code: +```py +for index, item in enumerate(my_list): + print(f"{index}: {item}") +``` +For more information, check out [the official docs](https://docs.python.org/3/library/functions.html#enumerate), or [PEP 279](https://www.python.org/dev/peps/pep-0279/). \ No newline at end of file diff --git a/bot/resources/tags/except.md b/bot/resources/tags/except.md new file mode 100644 index 000000000..66dce13ab --- /dev/null +++ b/bot/resources/tags/except.md @@ -0,0 +1,17 @@ +A key part of the Python philosophy is to ask for forgiveness, not permission. This means that it's okay to write code that may produce an error, as long as you specify how that error should be handled. Code written this way is readable and resilient. +```py +try: + number = int(user_input) +except ValueError: + print("failed to convert user_input to a number. setting number to 0.") + number = 0 +``` +You should always specify the exception type if it is possible to do so, and your `try` block should be as short as possible. Attempting to handle broad categories of unexpected exceptions can silently hide serious problems. +```py +try: + number = int(user_input) + item = some_list[number] +except: + print("An exception was raised, but we have no idea if it was a ValueError or an IndexError.") +``` +For more information about exception handling, see [the official Python docs](https://docs.python.org/3/tutorial/errors.html), or watch [Corey Schafer's video on exception handling](https://www.youtube.com/watch?v=NIWwJbo-9_8). \ No newline at end of file diff --git a/bot/resources/tags/exit().md b/bot/resources/tags/exit().md new file mode 100644 index 000000000..89f83f7e0 --- /dev/null +++ b/bot/resources/tags/exit().md @@ -0,0 +1,8 @@ +**Exiting Programmatically** + +If you want to exit your code programmatically, you might think to use the functions `exit()` or `quit()`, however this is bad practice. These functions are constants added by the [`site`](https://docs.python.org/3/library/site.html#module-site) module as a convenient method for exiting the interactive interpreter shell, and should not be used in programs. + +You should use either [`SystemExit`](https://docs.python.org/3/library/exceptions.html#SystemExit) or [`sys.exit()`](https://docs.python.org/3/library/sys.html#sys.exit) instead. +There's not much practical difference between these two other than having to `import sys` for the latter. Both take an optional argument to provide an exit status. + +[Official documentation](https://docs.python.org/3/library/constants.html#exit) with the warning not to use `exit()` or `quit()` in source code. \ No newline at end of file diff --git a/bot/resources/tags/f-strings.md b/bot/resources/tags/f-strings.md new file mode 100644 index 000000000..966fe6080 --- /dev/null +++ b/bot/resources/tags/f-strings.md @@ -0,0 +1,17 @@ +In Python, there are several ways to do string interpolation, including using `%s`\'s and by using the `+` operator to concatenate strings together. However, because some of these methods offer poor readability and require typecasting to prevent errors, you should for the most part be using a feature called format strings. + +**In Python 3.6 or later, we can use f-strings like this:** +```py +snake = "Pythons" +print(f"{snake} are some of the largest snakes in the world") +``` +**In earlier versions of Python or in projects where backwards compatibility is very important, use str.format() like this:** +```py +snake = "Pythons" + +# With str.format() you can either use indexes +print("{0} are some of the largest snakes in the world".format(snake)) + +# Or keyword arguments +print("{family} are some of the largest snakes in the world".format(family=snake)) +``` \ No newline at end of file diff --git a/bot/resources/tags/foo.md b/bot/resources/tags/foo.md new file mode 100644 index 000000000..58bc4b78f --- /dev/null +++ b/bot/resources/tags/foo.md @@ -0,0 +1,10 @@ +**Metasyntactic variables** + +A specific word or set of words identified as a placeholder used in programming. They are used to name entities such as variables, functions, etc, whose exact identity is unimportant and serve only to demonstrate a concept, which is useful for teaching programming. + +Common examples include `foobar`, `foo`, `bar`, `baz`, and `qux`. +Python has its own metasyntactic variables, namely `spam`, `eggs`, and `bacon`. This is a reference to a [Monty Python](https://en.wikipedia.org/wiki/Monty_Python) sketch (the eponym of the language). + +More information: +• [History of foobar](https://en.wikipedia.org/wiki/Foobar) +• [Monty Python sketch](https://en.wikipedia.org/wiki/Spam_%28Monty_Python%29) \ No newline at end of file diff --git a/bot/resources/tags/functions-are-objects.md b/bot/resources/tags/functions-are-objects.md new file mode 100644 index 000000000..d10e6b73e --- /dev/null +++ b/bot/resources/tags/functions-are-objects.md @@ -0,0 +1,39 @@ +**Calling vs. Referencing functions** + +When assigning a new name to a function, storing it in a container, or passing it as an argument, a common mistake made is to call the function. Instead of getting the actual function, you'll get its return value. + +In Python you can treat function names just like any other variable. Assume there was a function called `now` that returns the current time. If you did `x = now()`, the current time would be assigned to `x`, but if you did `x = now`, the function `now` itself would be assigned to `x`. `x` and `now` would both equally reference the function. + +**Examples** +```py +# assigning new name + +def foo(): + return 'bar' + +def spam(): + return 'eggs' + +baz = foo +baz() # returns 'bar' + +ham = spam +ham() # returns 'eggs' +``` +```py +# storing in container + +import math +functions = [math.sqrt, math.factorial, math.log] +functions[0](25) # returns 5.0 +# the above equivalent to math.sqrt(25) +``` +```py +# passing as argument + +class C: + builtin_open = staticmethod(open) + +# open function is passed +# to the staticmethod class +``` \ No newline at end of file diff --git a/bot/resources/tags/global.md b/bot/resources/tags/global.md new file mode 100644 index 000000000..fc60f9177 --- /dev/null +++ b/bot/resources/tags/global.md @@ -0,0 +1,16 @@ +When adding functions or classes to a program, it can be tempting to reference inaccessible variables by declaring them as global. Doing this can result in code that is harder to read, debug and test. Instead of using globals, pass variables or objects as parameters and receive return values. + +Instead of writing +```py +def update_score(): + global score, roll + score = score + roll +update_score() +``` +do this instead +```py +def update_score(score, roll): + return score + roll +score = update_score(score, roll) +``` +For in-depth explanations on why global variables are bad news in a variety of situations, see [this Stack Overflow answer](https://stackoverflow.com/questions/19158339/why-are-global-variables-evil/19158418#19158418). \ No newline at end of file diff --git a/bot/resources/tags/if-name-main.md b/bot/resources/tags/if-name-main.md new file mode 100644 index 000000000..d44f0086d --- /dev/null +++ b/bot/resources/tags/if-name-main.md @@ -0,0 +1,26 @@ +`if __name__ == '__main__'` + +This is a statement that is only true if the module (your source code) it appears in is being run directly, as opposed to being imported into another module. When you run your module, the `__name__` special variable is automatically set to the string `'__main__'`. Conversely, when you import that same module into a different one, and run that, `__name__` is instead set to the filename of your module minus the `.py` extension. + +**Example** +```py +# foo.py + +print('spam') + +if __name__ == '__main__': + print('eggs') +``` +If you run the above module `foo.py` directly, both `'spam'`and `'eggs'` will be printed. Now consider this next example: +```py +# bar.py + +import foo +``` +If you run this module named `bar.py`, it will execute the code in `foo.py`. First it will print `'spam'`, and then the `if` statement will fail, because `__name__` will now be the string `'foo'`. + +**Why would I do this?** + +• Your module is a library, but also has a special case where it can be run directly +• Your module is a library and you want to safeguard it against people running it directly (like what `pip` does) +• Your module is the main program, but has unit tests and the testing framework works by importing your module, and you want to avoid having your main code run during the test \ No newline at end of file diff --git a/bot/resources/tags/indent.md b/bot/resources/tags/indent.md new file mode 100644 index 000000000..5b36a4818 --- /dev/null +++ b/bot/resources/tags/indent.md @@ -0,0 +1,24 @@ +**Indentation** + +Indentation is leading whitespace (spaces and tabs) at the beginning of a line of code. In the case of Python, they are used to determine the grouping of statements. + +Spaces should be preferred over tabs. To be clear, this is in reference to the character itself, not the keys on a keyboard. Your editor/IDE should be configured to insert spaces when the TAB key is pressed. The amount of spaces should be a multiple of 4, except optionally in the case of continuation lines. + +**Example** +```py +def foo(): + bar = 'baz' # indented one level + if bar == 'baz': + print('ham') # indented two levels + return bar # indented one level +``` +The first line is not indented. The next two lines are indented to be inside of the function definition. They will only run when the function is called. The fourth line is indented to be inside the `if` statement, and will only run if the `if` statement evaluates to `True`. The fifth and last line is like the 2nd and 3rd and will always run when the function is called. It effectively closes the `if` statement above as no more lines can be inside the `if` statement below that line. + +**Indentation is used after:** +**1.** [Compound statements](https://docs.python.org/3/reference/compound_stmts.html) (eg. `if`, `while`, `for`, `try`, `with`, `def`, `class`, and their counterparts) +**2.** [Continuation lines](https://www.python.org/dev/peps/pep-0008/#indentation) + +**More Info** +**1.** [Indentation style guide](https://www.python.org/dev/peps/pep-0008/#indentation) +**2.** [Tabs or Spaces?](https://www.python.org/dev/peps/pep-0008/#tabs-or-spaces) +**3.** [Official docs on indentation](https://docs.python.org/3/reference/lexical_analysis.html#indentation) \ No newline at end of file diff --git a/bot/resources/tags/inline.md b/bot/resources/tags/inline.md new file mode 100644 index 000000000..4670256bc --- /dev/null +++ b/bot/resources/tags/inline.md @@ -0,0 +1,16 @@ +**Inline codeblocks** + +In addition to multi-line codeblocks, discord has support for inline codeblocks as well. These are small codeblocks that are usually a single line, that can fit between non-codeblocks on the same line. + +The following is an example of how it's done: + +The \`\_\_init\_\_\` method customizes the newly created instance. + +And results in the following: + +The `__init__` method customizes the newly created instance. + +**Note:** +• These are **backticks** not quotes +• Avoid using them for multiple lines +• Useful for negating formatting you don't want \ No newline at end of file diff --git a/bot/resources/tags/iterate-dict.md b/bot/resources/tags/iterate-dict.md new file mode 100644 index 000000000..b23475506 --- /dev/null +++ b/bot/resources/tags/iterate-dict.md @@ -0,0 +1,10 @@ +There are two common ways to iterate over a dictionary in Python. To iterate over the keys: +```py +for key in my_dict: + print(key) +``` +To iterate over both the keys and values: +```py +for key, val in my_dict.items(): + print(key, val) +``` \ No newline at end of file diff --git a/bot/resources/tags/listcomps.md b/bot/resources/tags/listcomps.md new file mode 100644 index 000000000..5ef0ce2bc --- /dev/null +++ b/bot/resources/tags/listcomps.md @@ -0,0 +1,14 @@ +Do you ever find yourself writing something like: +```py +even_numbers = [] +for n in range(20): + if n % 2 == 0: + even_numbers.append(n) +``` +Using list comprehensions can simplify this significantly, and greatly improve code readability. If we rewrite the example above to use list comprehensions, it would look like this: +```py +even_numbers = [n for n in range(20) if n % 2 == 0] +``` +This also works for generators, dicts and sets by using `()` or `{}` instead of `[]`. + +For more info, see [this pythonforbeginners.com post](http://www.pythonforbeginners.com/basics/list-comprehensions-in-python) or [PEP 202](https://www.python.org/dev/peps/pep-0202/). \ No newline at end of file diff --git a/bot/resources/tags/mutable-default-args.md b/bot/resources/tags/mutable-default-args.md new file mode 100644 index 000000000..49f536b78 --- /dev/null +++ b/bot/resources/tags/mutable-default-args.md @@ -0,0 +1,48 @@ +**Mutable Default Arguments** + +Default arguments in python are evaluated *once* when the function is +**defined**, *not* each time the function is **called**. This means that if +you have a mutable default argument and mutate it, you will have +mutated that object for all future calls to the function as well. + +For example, the following `append_one` function appends `1` to a list +and returns it. `foo` is set to an empty list by default. +```python +>>> def append_one(foo=[]): +... foo.append(1) +... return foo +... +``` +See what happens when we call it a few times: +```python +>>> append_one() +[1] +>>> append_one() +[1, 1] +>>> append_one() +[1, 1, 1] +``` +Each call appends an additional `1` to our list `foo`. It does not +receive a new empty list on each call, it is the same list everytime. + +To avoid this problem, you have to create a new object every time the +function is **called**: +```python +>>> def append_one(foo=None): +... if foo is None: +... foo = [] +... foo.append(1) +... return foo +... +>>> append_one() +[1] +>>> append_one() +[1] +``` + +**Note**: + +• This behavior can be used intentionally to maintain state between +calls of a function (eg. when writing a caching function). +• This behavior is not unique to mutable objects, all default +arguments are evaulated only once when the function is defined. \ No newline at end of file diff --git a/bot/resources/tags/names.md b/bot/resources/tags/names.md new file mode 100644 index 000000000..b7b914d53 --- /dev/null +++ b/bot/resources/tags/names.md @@ -0,0 +1,37 @@ +**Naming and Binding** + +A name is a piece of text that is bound to an object. They are a **reference** to an object. Examples are function names, class names, module names, variables, etc. + +**Note:** Names **cannot** reference other names, and assignment **never** creates a copy. +```py +x = 1 # x is bound to 1 +y = x # y is bound to VALUE of x +x = 2 # x is bound to 2 +print(x, y) # 2 1 +``` +When doing `y = x`, the name `y` is being bound to the *value* of `x` which is `1`. Neither `x` nor `y` are the 'real' name. The object `1` simply has *multiple* names. They are the exact same object. +``` +>>> x = 1 +x ━━ 1 + +>>> y = x +x ━━ 1 +y ━━━┛ + +>>> x = 2 +x ━━ 2 +y ━━ 1 +``` +**Names are created in multiple ways** +You might think that the only way to bind a name to an object is by using assignment, but that isn't the case. All of the following work exactly the same as assignment: +• `import` statements +• `class` and `def` +• `for` loop headers +• `as` keyword when used with `except`, `import`, and `with` +• formal parameters in function headers + +There is also `del` which has the purpose of *unbinding* a name. + +**More info** +• Please watch [Ned Batchelder's talk](https://youtu.be/_AEJHKGk9ns) on names in python for a detailed explanation with examples +• [Official documentation](https://docs.python.org/3/reference/executionmodel.html#naming-and-binding) \ No newline at end of file diff --git a/bot/resources/tags/off-topic.md b/bot/resources/tags/off-topic.md new file mode 100644 index 000000000..8fa70bf6e --- /dev/null +++ b/bot/resources/tags/off-topic.md @@ -0,0 +1,8 @@ +**Off-topic channels** + +There are three off-topic channels: +• <#291284109232308226> +• <#463035241142026251> +• <#463035268514185226> + +Their names change randomly every 24 hours, but you can always find them under the `OFF-TOPIC/GENERAL` category in the channel list. \ No newline at end of file diff --git a/bot/resources/tags/open.md b/bot/resources/tags/open.md new file mode 100644 index 000000000..74150dbc7 --- /dev/null +++ b/bot/resources/tags/open.md @@ -0,0 +1,26 @@ +**Opening files** + +The built-in function `open()` is one of several ways to open files on your computer. It accepts many different parameters, so this tag will only go over two of them (`file` and `mode`). For more extensive documentation on all these parameters, consult the [official documentation](https://docs.python.org/3/library/functions.html#open). The object returned from this function is a [file object or stream](https://docs.python.org/3/glossary.html#term-file-object), for which the full documentation can be found [here](https://docs.python.org/3/library/io.html#io.TextIOBase). + +See also: +• `!tags with` for information on context managers +• `!tags pathlib` for an alternative way of opening files +• `!tags seek` for information on changing your position in a file + +**The `file` parameter** + +This should be a [path-like object](https://docs.python.org/3/glossary.html#term-path-like-object) denoting the name or path (absolute or relative) to the file you want to open. + +An absolute path is the full path from your root directory to the file you want to open. Generally this is the option you should choose so it doesn't matter what directory you're in when you execute your module. + +See `!tags relative-path` for more information on relative paths. + +**The `mode` parameter** + +This is an optional string that specifies the mode in which the file should be opened. There's not enough room to discuss them all, but listed below are some of the more confusing modes. + +`'r+'` Opens for reading and writing (file must already exist) +`'w+'` Opens for reading and writing and truncates (can create files) +`'x'` Creates file and opens for writing (file must **not** already exist) +`'x+'` Creates file and opens for reading and writing (file must **not** already exist) +`'a+'` Opens file for reading and writing at **end of file** (can create files) \ No newline at end of file diff --git a/bot/resources/tags/or-gotcha.md b/bot/resources/tags/or-gotcha.md new file mode 100644 index 000000000..da82e3fdd --- /dev/null +++ b/bot/resources/tags/or-gotcha.md @@ -0,0 +1,17 @@ +When checking if something is equal to one thing or another, you might think that this is possible: +```py +if favorite_fruit == 'grapefruit' or 'lemon': + print("That's a weird favorite fruit to have.") +``` +After all, that's how you would normally phrase it in plain English. In Python, however, you have to have _complete instructions on both sides of the logical operator_. + +So, if you want to check if something is equal to one thing or another, there are two common ways: +```py +# Like this... +if favorite_fruit == 'grapefruit' or favorite_fruit == 'lemon': + print("That's a weird favorite fruit to have.") + +# ...or like this. +if favorite_fruit in ('grapefruit', 'lemon'): + print("That's a weird favorite fruit to have.") +``` \ No newline at end of file diff --git a/bot/resources/tags/param-arg.md b/bot/resources/tags/param-arg.md new file mode 100644 index 000000000..9e946812b --- /dev/null +++ b/bot/resources/tags/param-arg.md @@ -0,0 +1,12 @@ +**Parameters vs. Arguments** + +A parameter is a variable defined in a function signature (the line with `def` in it), while arguments are objects passed to a function call. + +```py +def square(n): # n is the parameter + return n*n + +print(square(5)) # 5 is the argument +``` + +Note that `5` is the argument passed to `square`, but `square(5)` in its entirety is the argument passed to `print` \ No newline at end of file diff --git a/bot/resources/tags/paste.md b/bot/resources/tags/paste.md new file mode 100644 index 000000000..d8e6e6c61 --- /dev/null +++ b/bot/resources/tags/paste.md @@ -0,0 +1,6 @@ +**Pasting large amounts of code** + +If your code is too long to fit in a codeblock in discord, you can paste your code here: +https://paste.pydis.com/ + +After pasting your code, **save** it by clicking the floppy disk icon in the top right, or by typing `ctrl + S`. After doing that, the URL should **change**. Copy the URL and post it here so others can see it. \ No newline at end of file diff --git a/bot/resources/tags/pathlib.md b/bot/resources/tags/pathlib.md new file mode 100644 index 000000000..37913951d --- /dev/null +++ b/bot/resources/tags/pathlib.md @@ -0,0 +1,21 @@ +**Pathlib** + +Python 3 comes with a new module named `Pathlib`. Since Python 3.6, `pathlib.Path` objects work nearly everywhere that `os.path` can be used, meaning you can integrate your new code directly into legacy code without having to rewrite anything. Pathlib makes working with paths way simpler than `os.path` does. + +**Feature spotlight**: + +• Normalizes file paths for all platforms automatically +• Has glob-like utilites (eg. `Path.glob`, `Path.rglob`) for searching files +• Can read and write files, and close them automatically +• Convenient syntax, utilising the `/` operator (e.g. `Path('~') / 'Documents'`) +• Can easily pick out components of a path (eg. name, parent, stem, suffix, anchor) +• Supports method chaining +• Move and delete files +• And much more + +**More Info**: + +• [**Why you should use pathlib** - Trey Hunner](https://treyhunner.com/2018/12/why-you-should-be-using-pathlib/) +• [**Answering concerns about pathlib** - Trey Hunner](https://treyhunner.com/2019/01/no-really-pathlib-is-great/) +• [**Official Documentation**](https://docs.python.org/3/library/pathlib.html) +• [**PEP 519** - Adding a file system path protocol](https://www.python.org/dev/peps/pep-0519/) \ No newline at end of file diff --git a/bot/resources/tags/pep8.md b/bot/resources/tags/pep8.md new file mode 100644 index 000000000..ec999bedc --- /dev/null +++ b/bot/resources/tags/pep8.md @@ -0,0 +1,3 @@ +**PEP 8** is the official style guide for Python. It includes comprehensive guidelines for code formatting, variable naming, and making your code easy to read. Professional Python developers are usually required to follow the guidelines, and will often use code-linters like `flake8` to verify that the code they\'re writing complies with the style guide. + +You can find the PEP 8 document [here](https://www.python.org/dev/peps/pep-0008). \ No newline at end of file diff --git a/bot/resources/tags/positional-keyword.md b/bot/resources/tags/positional-keyword.md new file mode 100644 index 000000000..3faec32ca --- /dev/null +++ b/bot/resources/tags/positional-keyword.md @@ -0,0 +1,38 @@ +**Positional vs. Keyword arguments** + +Functions can take two different kinds of arguments. A positional argument is just the object itself. A keyword argument is a name assigned to an object. + +**Example** +```py +>>> print('Hello', 'world!', sep=', ') +Hello, world! +``` +The first two strings `'Hello'` and `world!'` are positional arguments. +The `sep=', '` is a keyword argument. + +**Note** +A keyword argument can be passed positionally in some cases. +```py +def sum(a, b=1): + return a + b + +sum(1, b=5) +sum(1, 5) # same as above +``` +[Somtimes this is forced](https://www.python.org/dev/peps/pep-0570/#history-of-positional-only-parameter-semantics-in-python), in the case of the `pow()` function. + +The reverse is also true: +```py +>>> def foo(a, b): +... print(a, b) +... +>>> foo(a=1, b=2) +1 2 +>>> foo(b=1, a=2) +2 1 +``` + +**More info** +• [Keyword only arguments](https://www.python.org/dev/peps/pep-3102/) +• [Positional only arguments](https://www.python.org/dev/peps/pep-0570/) +• `!tags param-arg` (Parameters vs. Arguments) \ No newline at end of file diff --git a/bot/resources/tags/precedence.md b/bot/resources/tags/precedence.md new file mode 100644 index 000000000..8a4c66c4e --- /dev/null +++ b/bot/resources/tags/precedence.md @@ -0,0 +1,13 @@ +**Operator Precedence** + +Operator precedence is essentially like an order of operations for python's operators. + +**Example 1** (arithmetic) +`2 * 3 + 1` is `7` because multiplication is first +`2 * (3 + 1)` is `8` because the parenthesis change the precedence allowing the sum to be first + +**Example 2** (logic) +`not True or True` is `True` because the `not` is first +`not (True or True)` is `False` because the `or` is first + +The full table of precedence from lowest to highest is [here](https://docs.python.org/3/reference/expressions.html#operator-precedence) \ No newline at end of file diff --git a/bot/resources/tags/quotes.md b/bot/resources/tags/quotes.md new file mode 100644 index 000000000..609b6d2d2 --- /dev/null +++ b/bot/resources/tags/quotes.md @@ -0,0 +1,20 @@ +**String Quotes** + +Single and Double quoted strings are the **same** in Python. The choice of which one to use is up to you, just make sure that you **stick to that choice**. + +With that said, there are exceptions to this that are more important than consistency. If a single or double quote is needed *inside* the string, using the opposite quotation is better than using escape characters. + +Example: +```py +'My name is "Guido"' # good +"My name is \"Guido\"" # bad + +"Don't go in there" # good +'Don\'t go in there' # bad +``` +**Note:** +If you need both single and double quotes inside your string, use the version that would result in the least amount of escapes. In the case of a tie, use the quotation you use the most. + +**References:** +• [pep-8 on quotes](https://www.python.org/dev/peps/pep-0008/#string-quotes) +• [convention for triple quoted strings](https://www.python.org/dev/peps/pep-0257/) \ No newline at end of file diff --git a/bot/resources/tags/relative-path.md b/bot/resources/tags/relative-path.md new file mode 100644 index 000000000..269276e81 --- /dev/null +++ b/bot/resources/tags/relative-path.md @@ -0,0 +1,7 @@ +**Relative Path** + +A relative path is a partial path that is relative to your current working directory. A common misconception is that your current working directory is the location of the module you're executing, **but this is not the case**. Your current working directory is actually the **directory you were in when you ran the python interpreter**. The reason for this misconception is because a common way to run your code is to navigate to the directory your module is stored, and run `python .py`. Thus, in this case your current working directory will be the same as the location of the module. However, if we instead did `python path/to/.py`, our current working directory would no longer be the same as the location of the module we're executing. + +**Why is this important?** + +When opening files in python, relative paths won't always work since it's dependent on what directory you were in when you ran your code. A common issue people face is running their code in an IDE thinking they can open files that are in the same directory as their module, but the current working directory will be different than what they expect and so they won't find the file. The way to avoid this problem is by using absolute paths, which is the full path from your root directory to the file you want to open. \ No newline at end of file diff --git a/bot/resources/tags/repl.md b/bot/resources/tags/repl.md new file mode 100644 index 000000000..a68fe9397 --- /dev/null +++ b/bot/resources/tags/repl.md @@ -0,0 +1,13 @@ +**Read-Eval-Print Loop** + +A REPL is an interactive language shell environment. It first **reads** one or more expressions entered by the user, **evaluates** it, yields the result, and **prints** it out to the user. It will then **loop** back to the **read** step. + +To use python's REPL, execute the interpreter with no arguments. This will drop you into the interactive interpreter shell, print out some relevant information, and then prompt you with the primary prompt `>>>`. At this point it is waiting for your input. + +Firstly you can start typing in some valid python expressions, pressing to either bring you to the **eval** step, or prompting you with the secondary prompt `...` (or no prompt at all depending on your environment), meaning your expression isn't yet terminated and it's waiting for more input. This is useful for code that requires multiple lines like loops, functions, and classes. If you reach the secondary prompt in a clause that can have an arbitrary amount of expressions, you can terminate it by pressing on a blank line. In other words, for the last expression you write in the clause, must be pressed twice in a row. + +Alternatively, you can make use of the builtin `help()` function. `help(thing)` to get help on some `thing` object, or `help()` to start an interactive help session. This mode is extremely powerful, read the instructions when first entering the session to learn how to use it. + +Lastly you can run your code with the `-i` flag to execute your code normally, but be dropped into the REPL once execution is finished, giving you access to all your global variables/functions in the REPL. + +To **exit** either a help session, or normal REPL prompt, you must send an EOF signal to the prompt. In *nix systems, this is done with `ctrl + D`, and in windows systems it is `ctrl + Z`. You can also exit the normal REPL prompt with the dedicated functions `exit()` or `quit()`. \ No newline at end of file diff --git a/bot/resources/tags/return.md b/bot/resources/tags/return.md new file mode 100644 index 000000000..7e0cdaa98 --- /dev/null +++ b/bot/resources/tags/return.md @@ -0,0 +1,35 @@ +**Return Statement** + +When calling a function, you'll often want it to give you a value back. In order to do that, you must `return` it. The reason for this is because functions have their own scope. Any values defined within the function body are inaccessible outside of that function. + +*For more information about scope, see `!tags scope`* + +Consider the following function: +```py +def square(n): + return n*n +``` +If we wanted to store 5 squared in a variable called `x`, we could do that like so: +`x = square(5)`. `x` would now equal `25`. + +**Common Mistakes** +```py +>>> def square(n): +... n*n # calculates then throws away, returns None +... +>>> x = square(5) +>>> print(x) +None +>>> def square(n): +... print(n*n) # calculates and prints, then throws away and returns None +... +>>> x = square(5) +25 +>>> print(x) +None +``` +**Things to note** +• `print()` and `return` do **not** accomplish the same thing. `print()` will only print the value, it will not be accessible outside of the function afterwards. +• A function will return `None` if it ends without reaching an explicit `return` statement. +• When you want to print a value calculated in a function, instead of printing inside the function, it is often better to return the value and print the *function call* instead. +• [Official documentation for `return`](https://docs.python.org/3/reference/simple_stmts.html#the-return-statement) \ No newline at end of file diff --git a/bot/resources/tags/round.md b/bot/resources/tags/round.md new file mode 100644 index 000000000..3e33c8ff7 --- /dev/null +++ b/bot/resources/tags/round.md @@ -0,0 +1,24 @@ +**Round half to even** + +Python 3 uses bankers' rounding (also known by other names), where if the fractional part of a number is `.5`, it's rounded to the nearest **even** result instead of away from zero. + +Example: +```py +>>> round(2.5) +2 +>>> round(1.5) +2 +``` +In the first example, there is a tie between 2 and 3, and since 3 is odd and 2 is even, the result is 2. +In the second example, the tie is between 1 and 2, and so 2 is also the result. + +**Why this is done:** +The round half up technique creates a slight bias towards the larger number. With a large amount of calculations, this can be significant. The round half to even technique eliminates this bias. + +It should be noted that round half to even distorts the distribution by increasing the probability of evens relative to odds, however this is considered less important than the bias explained above. + +**References:** +• [Wikipedia article about rounding](https://en.wikipedia.org/wiki/Rounding#Round_half_to_even) +• [Documentation on `round` function](https://docs.python.org/3/library/functions.html#round) +• [`round` in what's new in python 3](https://docs.python.org/3/whatsnew/3.0.html#builtins) (4th bullet down) +• [How to force rounding technique](https://stackoverflow.com/a/10826537/4607272) \ No newline at end of file diff --git a/bot/resources/tags/scope.md b/bot/resources/tags/scope.md new file mode 100644 index 000000000..ff9d96637 --- /dev/null +++ b/bot/resources/tags/scope.md @@ -0,0 +1,24 @@ +**Scoping Rules** + +A *scope* defines the visibility of a name within a block, where a block is a piece of python code executed as a unit. For simplicity, this would be a module, a function body, and a class definition. A name refers to text bound to an object. + +*For more information about names, see `!tags names`* + +A module is the source code file itself, and encompasses all blocks defined within it. Therefore if a variable is defined at the module level (top-level code block), it is a global variable and can be accessed anywhere in the module as long as the block in which it's referenced is executed after it was defined. + +Alternatively if a variable is defined within a function block for example, it is a local variable. It is not accessible at the module level, as that would be *outside* its scope. This is the purpose of the `return` statement, as it hands an object back to the scope of its caller. Conversely if a function was defined *inside* the previously mentioned block, it *would* have access to that variable, because it is within the first function's scope. +```py +>>> def outer(): +... foo = 'bar' # local variable to outer +... def inner(): +... print(foo) # has access to foo from scope of outer +... return inner # brings inner to scope of caller +... +>>> inner = outer() # get inner function +>>> inner() # prints variable foo without issue +bar +``` +**Official Documentation** +**1.** [Program structure, name binding and resolution](https://docs.python.org/3/reference/executionmodel.html#execution-model) +**2.** [`global` statement](https://docs.python.org/3/reference/simple_stmts.html#the-global-statement) +**3.** [`nonlocal` statement](https://docs.python.org/3/reference/simple_stmts.html#the-nonlocal-statement) \ No newline at end of file diff --git a/bot/resources/tags/seek.md b/bot/resources/tags/seek.md new file mode 100644 index 000000000..ada23fd00 --- /dev/null +++ b/bot/resources/tags/seek.md @@ -0,0 +1,22 @@ +**Seek** + +In the context of a [file object](https://docs.python.org/3/glossary.html#term-file-object), the `seek` function changes the stream position to a given byte offset, with an optional argument of where to offset from. While you can find the official documentation [here](https://docs.python.org/3/library/io.html#io.IOBase.seek), it can be unclear how to actually use this feature, so keep reading to see examples on how to use it. + +File named `example`: +``` +foobar +spam eggs +``` +Open file for reading in byte mode: +```py +f = open('example', 'rb') +``` +Note that stream positions start from 0 in much the same way that the index for a list does. If we do `f.seek(3, 0)`, our stream position will move 3 bytes forward relative to the **beginning** of the stream. Now if we then did `f.read(1)` to read a single byte from where we are in the stream, it would return the string `'b'` from the 'b' in 'foobar'. Notice that the 'b' is the 4th character. Also note that after we did `f.read(1)`, we moved the stream position again 1 byte forward relative to the **current** position in the stream. So the stream position is now currently at position 4. + +Now lets do `f.seek(4, 1)`. This will move our stream position 4 bytes forward relative to our **current** position in the stream. Now if we did `f.read(1)`, it would return the string `'p'` from the 'p' in 'spam' on the next line. Note this time that the character at position 6 is the newline character `'\n'`. + +Finally, lets do `f.seek(-4, 2)`, moving our stream position *backwards* 4 bytes relative to the **end** of the stream. Now if we did `f.read()` to read everything after our position in the file, it would return the string `'eggs'` and also move our stream position to the end of the file. + +**Note** +• For the second argument in `seek()`, use `os.SEEK_SET`, `os.SEEK_CUR`, and `os.SEEK_END` in place of 0, 1, and 2 respectively. +• `os.SEEK_CUR` is only usable when the file is in byte mode. \ No newline at end of file diff --git a/bot/resources/tags/self.md b/bot/resources/tags/self.md new file mode 100644 index 000000000..a9cd5e9df --- /dev/null +++ b/bot/resources/tags/self.md @@ -0,0 +1,25 @@ +**Class instance** + +When calling a method from a class instance (ie. `instance.method()`), the instance itself will automatically be passed as the first argument implicitly. By convention, we call this `self`, but it could technically be called any valid variable name. + +```py +class Foo: + def bar(self): + print('bar') + + def spam(self, eggs): + print(eggs) + +foo = Foo() +``` + +If we call `foo.bar()`, it is equivalent to doing `Foo.bar(foo)`. Our instance `foo` is passed for us to the `bar` function, so while we initially gave zero arguments, it is actually called with one. + +Similarly if we call `foo.spam('ham')`, it is equivalent to +doing `Foo.spam(foo, 'ham')`. + +**Why is this useful?** + +Methods do not inherently have access to attributes defined in the class. In order for any one method to be able to access other methods or variables defined in the class, it must have access to the instance. + +Consider if outside the class, we tried to do this: `spam(foo, 'ham')`. This would give an error, because we don't have access to the `spam` method directly, we have to call it by doing `foo.spam('ham')`. This is also the case inside of the class. If we wanted to call the `bar` method inside the `spam` method, we'd have to do `self.bar()`, just doing `bar()` would give an error. \ No newline at end of file diff --git a/bot/resources/tags/star-imports.md b/bot/resources/tags/star-imports.md new file mode 100644 index 000000000..4c7e0199c --- /dev/null +++ b/bot/resources/tags/star-imports.md @@ -0,0 +1,48 @@ +**Star / Wildcard imports** + +Wildcard imports are import statements in the form `from import *`. What imports like these do is that they import everything **[1]** from the module into the current module's namespace **[2]**. This allows you to use names defined in the imported module without prefixing the module's name. + +Example: +```python +>>> from math import * +>>> sin(pi / 2) +1.0 +``` +**This is discouraged, for various reasons:** + +Example: +```python +>>> from custom_sin import sin +>>> from math import * +>>> sin(pi / 2) # uses sin from math rather than your custom sin +``` + +• Potential namespace collision. Names defined from a previous import might get shadowed by a wildcard import. + +• Causes ambiguity. From the example, it is unclear which `sin` function is actually being used. From the Zen of Python **[3]**: `Explicit is better than implicit.` + +• Makes import order significant, which they shouldn't. Certain IDE's `sort import` functionality may end up breaking code due to namespace collision. + +**How should you import?** + +• Import the module under the module's namespace (Only import the name of the module, and names defined in the module can be used by prefixing the module's name) + +```python +>>> import math +>>> math.sin(math.pi / 2) +``` + +• Explicitly import certain names from the module + +```python +>>> from math import sin, pi +>>> sin(pi / 2) +``` + +Conclusion: Namespaces are one honking great idea -- let's do more of those! *[3]* + +**[1]** If the module defines the variable `__all__`, the names defined in `__all__` will get imported by the wildcard import, otherwise all the names in the module get imported (except for names with a leading underscore) + +**[2]** [Namespaces and scopes](https://www.programiz.com/python-programming/namespace) + +**[3]** [Zen of Python](https://www.python.org/dev/peps/pep-0020/) \ No newline at end of file diff --git a/bot/resources/tags/traceback.md b/bot/resources/tags/traceback.md new file mode 100644 index 000000000..74401abf0 --- /dev/null +++ b/bot/resources/tags/traceback.md @@ -0,0 +1,18 @@ +Please provide a full traceback to your exception in order for us to identify your issue. + +A full traceback could look like: +```py +Traceback (most recent call last): + File "tiny", line 3, in + do_something() + File "tiny", line 2, in do_something + a = 6 / 0 +ZeroDivisionError: integer division or modulo by zero +``` +The best way to read your traceback is bottom to top. + +• Identify the exception raised (e.g. ZeroDivisonError) +• Make note of the line number, and navigate there in your program. +• Try to understand why the error occurred. + +To read more about exceptions and errors, please refer to the [PyDis Wiki](https://pythondiscord.com/pages/asking-good-questions/#examining-tracebacks) or the [official Python tutorial.](https://docs.python.org/3.7/tutorial/errors.html) \ No newline at end of file diff --git a/bot/resources/tags/windows-path.md b/bot/resources/tags/windows-path.md new file mode 100644 index 000000000..d8723f06f --- /dev/null +++ b/bot/resources/tags/windows-path.md @@ -0,0 +1,30 @@ +**PATH on Windows** + +If you have installed Python but you forgot to check the *Add Python to PATH* option during the installation you may still be able to access your installation with ease. + +If you did not uncheck the option to install the Python launcher then you will find a `py` command on your system. If you want to be able to open your Python installation by running `python` then your best option is to re-install Python. + +Otherwise, you can access your install using the `py` command in Command Prompt. Where you may type something with the `python` command like: +``` +C:\Users\Username> python3 my_application_file.py +``` + +You can achieve the same result using the `py` command like this: +``` +C:\Users\Username> py -3 my_application_file.py +``` + +You can pass any options to the Python interpreter after you specify a version, for example, to install a Python module using `pip` you can run: +``` +C:\Users\Username> py -3 -m pip install numpy +``` + +You can also access different versions of Python using the version flag, like so: +``` +C:\Users\Username> py -3.7 +... Python 3.7 starts ... +C:\Users\Username> py -3.6 +... Python 3.6 stars ... +C:\Users\Username> py -2 +... Python 2 (any version installed) starts ... +``` \ No newline at end of file diff --git a/bot/resources/tags/with.md b/bot/resources/tags/with.md new file mode 100644 index 000000000..a79eb7dbb --- /dev/null +++ b/bot/resources/tags/with.md @@ -0,0 +1,8 @@ +The `with` keyword triggers a context manager. Context managers automatically set up and take down data connections, or any other kind of object that implements the magic methods `__enter__` and `__exit__`. +```py +with open("test.txt", "r") as file: + do_things(file) +``` +The above code automatically closes `file` when the `with` block exits, so you never have to manually do a `file.close()`. Most connection types, including file readers and database connections, support this. + +For more information, read [the official docs](https://docs.python.org/3/reference/compound_stmts.html#with), watch [Corey Schafer\'s context manager video](https://www.youtube.com/watch?v=-aKFBoZpiqA), or see [PEP 343](https://www.python.org/dev/peps/pep-0343/). \ No newline at end of file diff --git a/bot/resources/tags/xy-problem.md b/bot/resources/tags/xy-problem.md new file mode 100644 index 000000000..77700e7a0 --- /dev/null +++ b/bot/resources/tags/xy-problem.md @@ -0,0 +1,7 @@ +**xy-problem** + +Asking about your attempted solution rather than your actual problem. + +Often programmers will get distracted with a potential solution they've come up with, and will try asking for help getting it to work. However, it's possible this solution either wouldn't work as they expect, or there's a much better solution instead. + +For more information and examples: http://xyproblem.info/ \ No newline at end of file diff --git a/bot/resources/tags/ytdl.md b/bot/resources/tags/ytdl.md new file mode 100644 index 000000000..e1085d1af --- /dev/null +++ b/bot/resources/tags/ytdl.md @@ -0,0 +1,9 @@ +Per [PyDis' Rule 5](https://pythondiscord.com/pages/rules), we are unable to assist with questions related to youtube-dl, commonly used by Discord bots to stream audio, as its use violates YouTube's Terms of Service. + +For reference, this usage is covered by the following clauses in [YouTube's TOS](https://www.youtube.com/static?template=terms), as of 2018-05-25: +``` +4A: You agree not to distribute in any medium any part of the Service or the Content without YouTube's prior written authorization, unless YouTube makes available the means for such distribution through functionality offered by the Service (such as the Embeddable Player). +``` +``` +4C: You agree not to access Content through any technology or means other than the video playback pages of the Service itself, the Embeddable Player, or other explicitly authorized means YouTube may designate. +``` \ No newline at end of file diff --git a/bot/resources/tags/zen.md b/bot/resources/tags/zen.md new file mode 100644 index 000000000..3e132eed8 --- /dev/null +++ b/bot/resources/tags/zen.md @@ -0,0 +1,20 @@ + +Beautiful is better than ugly. +Explicit is better than implicit. +Simple is better than complex. +Complex is better than complicated. +Flat is better than nested. +Sparse is better than dense. +Readability counts. +Special cases aren't special enough to break the rules. +Although practicality beats purity. +Errors should never pass silently. +Unless explicitly silenced. +In the face of ambiguity, refuse the temptation to guess. +There should be one-- and preferably only one --obvious way to do it. +Although that way may not be obvious at first unless you're Dutch. +Now is better than never. +Although never is often better than *right* now. +If the implementation is hard to explain, it's a bad idea. +If the implementation is easy to explain, it may be a good idea. +Namespaces are one honking great idea -- let's do more of those! diff --git a/bot/resources/tags/zip.md b/bot/resources/tags/zip.md new file mode 100644 index 000000000..9d2fe5ee3 --- /dev/null +++ b/bot/resources/tags/zip.md @@ -0,0 +1,12 @@ +The zip function allows you to iterate through multiple iterables simultaneously. It joins the iterables together, almost like a zipper, so that each new element is a tuple with one element from each iterable. + +```py +letters = 'abc' +numbers = [1, 2, 3] +# zip(letters, numbers) --> [('a', 1), ('b', 2), ('c', 3)] +for letter, number in zip(letters, numbers): + print(letter, number) +``` +The `zip()` iterator is exhausted after the length of the shortest iterable is exceeded. If you would like to retain the other values, consider using [itertools.zip_longest](https://docs.python.org/3/library/itertools.html#itertools.zip_longest). + +For more information on zip, please refer to the [official documentation](https://docs.python.org/3/library/functions.html#zip). \ No newline at end of file -- cgit v1.2.3 From f2563465396ab381d85f98b55b7a91b2ad00ed04 Mon Sep 17 00:00:00 2001 From: RohanJnr Date: Sat, 29 Feb 2020 14:50:45 +0530 Subject: added white spaces on statements before bullet points for proper rendering of points on github --- bot/resources/tags/args-kwargs.md | 10 +++++----- bot/resources/tags/ask.md | 8 ++++---- bot/resources/tags/codeblock.md | 8 ++++---- bot/resources/tags/decorators.md | 6 +++--- bot/resources/tags/foo.md | 4 ++-- bot/resources/tags/inline.md | 6 +++--- bot/resources/tags/mutable-default-args.md | 2 +- bot/resources/tags/names.md | 18 +++++++++--------- bot/resources/tags/off-topic.md | 8 ++++---- bot/resources/tags/open.md | 8 ++++---- bot/resources/tags/pathlib.md | 24 ++++++++++++------------ bot/resources/tags/positional-keyword.md | 8 ++++---- bot/resources/tags/quotes.md | 4 ++-- bot/resources/tags/return.md | 10 +++++----- bot/resources/tags/round.md | 10 +++++----- bot/resources/tags/scope.md | 6 +++--- bot/resources/tags/seek.md | 4 ++-- bot/resources/tags/traceback.md | 6 +++--- 18 files changed, 75 insertions(+), 75 deletions(-) diff --git a/bot/resources/tags/args-kwargs.md b/bot/resources/tags/args-kwargs.md index fb19d39fd..de883dea8 100644 --- a/bot/resources/tags/args-kwargs.md +++ b/bot/resources/tags/args-kwargs.md @@ -8,10 +8,10 @@ These special parameters allow functions to take arbitrary amounts of positional **Double asterisk** `**kwargs` will ingest an arbitrary amount of **keyword arguments**, and store it in a dictionary. There can be **no** additional parameters **after** `**kwargs` in the parameter list. -**Use cases** -• **Decorators** (see `!tags decorators`) -• **Inheritance** (overriding methods) -• **Future proofing** (in the case of the first two bullet points, if the parameters change, your code won't break) -• **Flexibility** (writing functions that behave like `dict()` or `print()`) +**Use cases** +• **Decorators** (see `!tags decorators`) +• **Inheritance** (overriding methods) +• **Future proofing** (in the case of the first two bullet points, if the parameters change, your code won't break) +• **Flexibility** (writing functions that behave like `dict()` or `print()`) *See* `!tags positional-keyword` *for information about positional and keyword arguments* \ No newline at end of file diff --git a/bot/resources/tags/ask.md b/bot/resources/tags/ask.md index 07f9bd84d..ed651e8c5 100644 --- a/bot/resources/tags/ask.md +++ b/bot/resources/tags/ask.md @@ -1,9 +1,9 @@ Asking good questions will yield a much higher chance of a quick response: -• Don't ask to ask your question, just go ahead and tell us your problem. -• Don't ask if anyone is knowledgeable in some area, filtering serves no purpose. -• Try to solve the problem on your own first, we're not going to write code for you. -• Show us the code you've tried and any errors or unexpected results it's giving. +• Don't ask to ask your question, just go ahead and tell us your problem. +• Don't ask if anyone is knowledgeable in some area, filtering serves no purpose. +• Try to solve the problem on your own first, we're not going to write code for you. +• Show us the code you've tried and any errors or unexpected results it's giving. • Be patient while we're helping you. You can find a much more detailed explanation [on our website](https://pythondiscord.com/pages/asking-good-questions/). \ No newline at end of file diff --git a/bot/resources/tags/codeblock.md b/bot/resources/tags/codeblock.md index 816bb8232..34db060ef 100644 --- a/bot/resources/tags/codeblock.md +++ b/bot/resources/tags/codeblock.md @@ -6,10 +6,10 @@ To do this, use the following method: print('Hello world!') \``` -Note: -• **These are backticks, not quotes.** Backticks can usually be found on the tilde key. -• You can also use py as the language instead of python -• The language must be on the first line next to the backticks with **no** space between them +Note: +• **These are backticks, not quotes.** Backticks can usually be found on the tilde key. +• You can also use py as the language instead of python +• The language must be on the first line next to the backticks with **no** space between them This will result in the following: ```py diff --git a/bot/resources/tags/decorators.md b/bot/resources/tags/decorators.md index 3ff1db16c..9b53af064 100644 --- a/bot/resources/tags/decorators.md +++ b/bot/resources/tags/decorators.md @@ -26,6 +26,6 @@ Time elapsed: 3.000307321548462 Finished! ``` -More information: -• [Corey Schafer's video on decorators](https://youtu.be/FsAPt_9Bf3U) -• [Real python article](https://realpython.com/primer-on-python-decorators/) \ No newline at end of file +More information: +• [Corey Schafer's video on decorators](https://youtu.be/FsAPt_9Bf3U) +• [Real python article](https://realpython.com/primer-on-python-decorators/) \ No newline at end of file diff --git a/bot/resources/tags/foo.md b/bot/resources/tags/foo.md index 58bc4b78f..2b5b659fd 100644 --- a/bot/resources/tags/foo.md +++ b/bot/resources/tags/foo.md @@ -5,6 +5,6 @@ A specific word or set of words identified as a placeholder used in programming. Common examples include `foobar`, `foo`, `bar`, `baz`, and `qux`. Python has its own metasyntactic variables, namely `spam`, `eggs`, and `bacon`. This is a reference to a [Monty Python](https://en.wikipedia.org/wiki/Monty_Python) sketch (the eponym of the language). -More information: -• [History of foobar](https://en.wikipedia.org/wiki/Foobar) +More information: +• [History of foobar](https://en.wikipedia.org/wiki/Foobar) • [Monty Python sketch](https://en.wikipedia.org/wiki/Spam_%28Monty_Python%29) \ No newline at end of file diff --git a/bot/resources/tags/inline.md b/bot/resources/tags/inline.md index 4670256bc..d0c9d1b5e 100644 --- a/bot/resources/tags/inline.md +++ b/bot/resources/tags/inline.md @@ -10,7 +10,7 @@ And results in the following: The `__init__` method customizes the newly created instance. -**Note:** -• These are **backticks** not quotes -• Avoid using them for multiple lines +**Note:** +• These are **backticks** not quotes +• Avoid using them for multiple lines • Useful for negating formatting you don't want \ No newline at end of file diff --git a/bot/resources/tags/mutable-default-args.md b/bot/resources/tags/mutable-default-args.md index 49f536b78..7b16e6b82 100644 --- a/bot/resources/tags/mutable-default-args.md +++ b/bot/resources/tags/mutable-default-args.md @@ -43,6 +43,6 @@ function is **called**: **Note**: • This behavior can be used intentionally to maintain state between -calls of a function (eg. when writing a caching function). +calls of a function (eg. when writing a caching function). • This behavior is not unique to mutable objects, all default arguments are evaulated only once when the function is defined. \ No newline at end of file diff --git a/bot/resources/tags/names.md b/bot/resources/tags/names.md index b7b914d53..462c550bc 100644 --- a/bot/resources/tags/names.md +++ b/bot/resources/tags/names.md @@ -22,16 +22,16 @@ y ━━━┛ x ━━ 2 y ━━ 1 ``` -**Names are created in multiple ways** -You might think that the only way to bind a name to an object is by using assignment, but that isn't the case. All of the following work exactly the same as assignment: -• `import` statements -• `class` and `def` -• `for` loop headers -• `as` keyword when used with `except`, `import`, and `with` -• formal parameters in function headers +**Names are created in multiple ways** +You might think that the only way to bind a name to an object is by using assignment, but that isn't the case. All of the following work exactly the same as assignment: +• `import` statements +• `class` and `def` +• `for` loop headers +• `as` keyword when used with `except`, `import`, and `with` +• formal parameters in function headers There is also `del` which has the purpose of *unbinding* a name. -**More info** -• Please watch [Ned Batchelder's talk](https://youtu.be/_AEJHKGk9ns) on names in python for a detailed explanation with examples +**More info** +• Please watch [Ned Batchelder's talk](https://youtu.be/_AEJHKGk9ns) on names in python for a detailed explanation with examples • [Official documentation](https://docs.python.org/3/reference/executionmodel.html#naming-and-binding) \ No newline at end of file diff --git a/bot/resources/tags/off-topic.md b/bot/resources/tags/off-topic.md index 8fa70bf6e..004adfa17 100644 --- a/bot/resources/tags/off-topic.md +++ b/bot/resources/tags/off-topic.md @@ -1,8 +1,8 @@ **Off-topic channels** -There are three off-topic channels: -• <#291284109232308226> -• <#463035241142026251> -• <#463035268514185226> +There are three off-topic channels: +• <#291284109232308226> +• <#463035241142026251> +• <#463035268514185226> Their names change randomly every 24 hours, but you can always find them under the `OFF-TOPIC/GENERAL` category in the channel list. \ No newline at end of file diff --git a/bot/resources/tags/open.md b/bot/resources/tags/open.md index 74150dbc7..1ba19dedd 100644 --- a/bot/resources/tags/open.md +++ b/bot/resources/tags/open.md @@ -2,10 +2,10 @@ The built-in function `open()` is one of several ways to open files on your computer. It accepts many different parameters, so this tag will only go over two of them (`file` and `mode`). For more extensive documentation on all these parameters, consult the [official documentation](https://docs.python.org/3/library/functions.html#open). The object returned from this function is a [file object or stream](https://docs.python.org/3/glossary.html#term-file-object), for which the full documentation can be found [here](https://docs.python.org/3/library/io.html#io.TextIOBase). -See also: -• `!tags with` for information on context managers -• `!tags pathlib` for an alternative way of opening files -• `!tags seek` for information on changing your position in a file +See also: +• `!tags with` for information on context managers +• `!tags pathlib` for an alternative way of opening files +• `!tags seek` for information on changing your position in a file **The `file` parameter** diff --git a/bot/resources/tags/pathlib.md b/bot/resources/tags/pathlib.md index 37913951d..468945cc5 100644 --- a/bot/resources/tags/pathlib.md +++ b/bot/resources/tags/pathlib.md @@ -4,18 +4,18 @@ Python 3 comes with a new module named `Pathlib`. Since Python 3.6, `pathlib.Pat **Feature spotlight**: -• Normalizes file paths for all platforms automatically -• Has glob-like utilites (eg. `Path.glob`, `Path.rglob`) for searching files -• Can read and write files, and close them automatically -• Convenient syntax, utilising the `/` operator (e.g. `Path('~') / 'Documents'`) -• Can easily pick out components of a path (eg. name, parent, stem, suffix, anchor) -• Supports method chaining -• Move and delete files -• And much more +• Normalizes file paths for all platforms automatically +• Has glob-like utilites (eg. `Path.glob`, `Path.rglob`) for searching files +• Can read and write files, and close them automatically +• Convenient syntax, utilising the `/` operator (e.g. `Path('~') / 'Documents'`) +• Can easily pick out components of a path (eg. name, parent, stem, suffix, anchor) +• Supports method chaining +• Move and delete files +• And much more **More Info**: -• [**Why you should use pathlib** - Trey Hunner](https://treyhunner.com/2018/12/why-you-should-be-using-pathlib/) -• [**Answering concerns about pathlib** - Trey Hunner](https://treyhunner.com/2019/01/no-really-pathlib-is-great/) -• [**Official Documentation**](https://docs.python.org/3/library/pathlib.html) -• [**PEP 519** - Adding a file system path protocol](https://www.python.org/dev/peps/pep-0519/) \ No newline at end of file +• [**Why you should use pathlib** - Trey Hunner](https://treyhunner.com/2018/12/why-you-should-be-using-pathlib/) +• [**Answering concerns about pathlib** - Trey Hunner](https://treyhunner.com/2019/01/no-really-pathlib-is-great/) +• [**Official Documentation**](https://docs.python.org/3/library/pathlib.html) +• [**PEP 519** - Adding a file system path protocol](https://www.python.org/dev/peps/pep-0519/) \ No newline at end of file diff --git a/bot/resources/tags/positional-keyword.md b/bot/resources/tags/positional-keyword.md index 3faec32ca..bc7f68ee0 100644 --- a/bot/resources/tags/positional-keyword.md +++ b/bot/resources/tags/positional-keyword.md @@ -32,7 +32,7 @@ The reverse is also true: 2 1 ``` -**More info** -• [Keyword only arguments](https://www.python.org/dev/peps/pep-3102/) -• [Positional only arguments](https://www.python.org/dev/peps/pep-0570/) -• `!tags param-arg` (Parameters vs. Arguments) \ No newline at end of file +**More info** +• [Keyword only arguments](https://www.python.org/dev/peps/pep-3102/) +• [Positional only arguments](https://www.python.org/dev/peps/pep-0570/) +• `!tags param-arg` (Parameters vs. Arguments) \ No newline at end of file diff --git a/bot/resources/tags/quotes.md b/bot/resources/tags/quotes.md index 609b6d2d2..bb6e2a009 100644 --- a/bot/resources/tags/quotes.md +++ b/bot/resources/tags/quotes.md @@ -15,6 +15,6 @@ Example: **Note:** If you need both single and double quotes inside your string, use the version that would result in the least amount of escapes. In the case of a tie, use the quotation you use the most. -**References:** -• [pep-8 on quotes](https://www.python.org/dev/peps/pep-0008/#string-quotes) +**References:** +• [pep-8 on quotes](https://www.python.org/dev/peps/pep-0008/#string-quotes) • [convention for triple quoted strings](https://www.python.org/dev/peps/pep-0257/) \ No newline at end of file diff --git a/bot/resources/tags/return.md b/bot/resources/tags/return.md index 7e0cdaa98..c944dddf2 100644 --- a/bot/resources/tags/return.md +++ b/bot/resources/tags/return.md @@ -28,8 +28,8 @@ None >>> print(x) None ``` -**Things to note** -• `print()` and `return` do **not** accomplish the same thing. `print()` will only print the value, it will not be accessible outside of the function afterwards. -• A function will return `None` if it ends without reaching an explicit `return` statement. -• When you want to print a value calculated in a function, instead of printing inside the function, it is often better to return the value and print the *function call* instead. -• [Official documentation for `return`](https://docs.python.org/3/reference/simple_stmts.html#the-return-statement) \ No newline at end of file +**Things to note** +• `print()` and `return` do **not** accomplish the same thing. `print()` will only print the value, it will not be accessible outside of the function afterwards. +• A function will return `None` if it ends without reaching an explicit `return` statement. +• When you want to print a value calculated in a function, instead of printing inside the function, it is often better to return the value and print the *function call* instead. +• [Official documentation for `return`](https://docs.python.org/3/reference/simple_stmts.html#the-return-statement) \ No newline at end of file diff --git a/bot/resources/tags/round.md b/bot/resources/tags/round.md index 3e33c8ff7..28a12469a 100644 --- a/bot/resources/tags/round.md +++ b/bot/resources/tags/round.md @@ -17,8 +17,8 @@ The round half up technique creates a slight bias towards the larger number. Wit It should be noted that round half to even distorts the distribution by increasing the probability of evens relative to odds, however this is considered less important than the bias explained above. -**References:** -• [Wikipedia article about rounding](https://en.wikipedia.org/wiki/Rounding#Round_half_to_even) -• [Documentation on `round` function](https://docs.python.org/3/library/functions.html#round) -• [`round` in what's new in python 3](https://docs.python.org/3/whatsnew/3.0.html#builtins) (4th bullet down) -• [How to force rounding technique](https://stackoverflow.com/a/10826537/4607272) \ No newline at end of file +**References:** +• [Wikipedia article about rounding](https://en.wikipedia.org/wiki/Rounding#Round_half_to_even) +• [Documentation on `round` function](https://docs.python.org/3/library/functions.html#round) +• [`round` in what's new in python 3](https://docs.python.org/3/whatsnew/3.0.html#builtins) (4th bullet down) +• [How to force rounding technique](https://stackoverflow.com/a/10826537/4607272) \ No newline at end of file diff --git a/bot/resources/tags/scope.md b/bot/resources/tags/scope.md index ff9d96637..c1eeb3b84 100644 --- a/bot/resources/tags/scope.md +++ b/bot/resources/tags/scope.md @@ -18,7 +18,7 @@ Alternatively if a variable is defined within a function block for example, it i >>> inner() # prints variable foo without issue bar ``` -**Official Documentation** -**1.** [Program structure, name binding and resolution](https://docs.python.org/3/reference/executionmodel.html#execution-model) -**2.** [`global` statement](https://docs.python.org/3/reference/simple_stmts.html#the-global-statement) +**Official Documentation** +**1.** [Program structure, name binding and resolution](https://docs.python.org/3/reference/executionmodel.html#execution-model) +**2.** [`global` statement](https://docs.python.org/3/reference/simple_stmts.html#the-global-statement) **3.** [`nonlocal` statement](https://docs.python.org/3/reference/simple_stmts.html#the-nonlocal-statement) \ No newline at end of file diff --git a/bot/resources/tags/seek.md b/bot/resources/tags/seek.md index ada23fd00..ff6569a0c 100644 --- a/bot/resources/tags/seek.md +++ b/bot/resources/tags/seek.md @@ -17,6 +17,6 @@ Now lets do `f.seek(4, 1)`. This will move our stream position 4 bytes forward r Finally, lets do `f.seek(-4, 2)`, moving our stream position *backwards* 4 bytes relative to the **end** of the stream. Now if we did `f.read()` to read everything after our position in the file, it would return the string `'eggs'` and also move our stream position to the end of the file. -**Note** -• For the second argument in `seek()`, use `os.SEEK_SET`, `os.SEEK_CUR`, and `os.SEEK_END` in place of 0, 1, and 2 respectively. +**Note** +• For the second argument in `seek()`, use `os.SEEK_SET`, `os.SEEK_CUR`, and `os.SEEK_END` in place of 0, 1, and 2 respectively. • `os.SEEK_CUR` is only usable when the file is in byte mode. \ No newline at end of file diff --git a/bot/resources/tags/traceback.md b/bot/resources/tags/traceback.md index 74401abf0..678ba1991 100644 --- a/bot/resources/tags/traceback.md +++ b/bot/resources/tags/traceback.md @@ -11,8 +11,8 @@ ZeroDivisionError: integer division or modulo by zero ``` The best way to read your traceback is bottom to top. -• Identify the exception raised (e.g. ZeroDivisonError) -• Make note of the line number, and navigate there in your program. -• Try to understand why the error occurred. +• Identify the exception raised (e.g. ZeroDivisonError) +• Make note of the line number, and navigate there in your program. +• Try to understand why the error occurred. To read more about exceptions and errors, please refer to the [PyDis Wiki](https://pythondiscord.com/pages/asking-good-questions/#examining-tracebacks) or the [official Python tutorial.](https://docs.python.org/3.7/tutorial/errors.html) \ No newline at end of file -- cgit v1.2.3 From fe31808089aa01c9e495d16d5c0cdbc4640a5ded Mon Sep 17 00:00:00 2001 From: RohanJnr Date: Sat, 29 Feb 2020 15:19:37 +0530 Subject: Re-corrected the lines which I had changed by mistake --- bot/cogs/tags.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/cogs/tags.py b/bot/cogs/tags.py index 0e959b45f..b62289e38 100644 --- a/bot/cogs/tags.py +++ b/bot/cogs/tags.py @@ -16,7 +16,8 @@ from bot.pagination import LinePaginator log = logging.getLogger(__name__) TEST_CHANNELS = ( - Channels.bot_commands, + Channels.devtest, + Channels.bot, Channels.helpers ) -- cgit v1.2.3 From 1b568681575d70d5b8dc5e8449e71a928896076c Mon Sep 17 00:00:00 2001 From: RohanJnr Date: Sat, 29 Feb 2020 21:48:50 +0530 Subject: Caching all the tags when the bot has loaded(caching only once) insted of caching it after the tags command is used. --- bot/cogs/tags.py | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/bot/cogs/tags.py b/bot/cogs/tags.py index b62289e38..3cab8c11f 100644 --- a/bot/cogs/tags.py +++ b/bot/cogs/tags.py @@ -31,27 +31,27 @@ class Tags(Cog): self.bot = bot self.tag_cooldowns = {} self._cache = {} - self._last_fetch: float = 0.0 - async def _get_tags(self, is_forced: bool = False) -> None: + @Cog.listener() + async def on_ready(self) -> None: + """Runs the code before the bot has connected.""" + await self.get_tags() + + async def get_tags(self) -> None: """Get all tags.""" - # refresh only when there's a more than 5m gap from last call. - time_now: float = time.time() - if is_forced or not self._last_fetch or time_now - self._last_fetch > 5 * 60: - tag_files = os.listdir("bot/resources/tags") - for file in tag_files: - p = Path("bot", "resources", "tags", file) - tag_title = os.path.splitext(file)[0].lower() - with p.open() as f: - tag = { - "title": tag_title, - "embed": { - "description": f.read() - } + # Save all tags in memory. + tag_files = os.listdir("bot/resources/tags") + for file in tag_files: + p = Path("bot", "resources", "tags", file) + tag_title = os.path.splitext(file)[0].lower() + with p.open() as f: + tag = { + "title": tag_title, + "embed": { + "description": f.read() } - self._cache[tag_title] = tag - - self._last_fetch = time_now + } + self._cache[tag_title] = tag @staticmethod def _fuzzy_search(search: str, target: str) -> float: @@ -92,7 +92,6 @@ class Tags(Cog): async def _get_tag(self, tag_name: str) -> list: """Get a specific tag.""" - await self._get_tags() found = [self._cache.get(tag_name.lower(), None)] if not found[0]: return self._get_suggestions(tag_name) @@ -133,8 +132,6 @@ class Tags(Cog): ) return - await self._get_tags() - if tag_name is not None: founds = await self._get_tag(tag_name) -- cgit v1.2.3 From 1c7b2d8a212ee837adf5dedb617310fea3b45080 Mon Sep 17 00:00:00 2001 From: RohanJnr Date: Wed, 4 Mar 2020 23:30:37 +0530 Subject: Use "pathlib" instead of "os" module and context manager The pathlib module simplifies opening and reading files, hence the os module and the context manager are no longer used. --- bot/cogs/tags.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/bot/cogs/tags.py b/bot/cogs/tags.py index 1c6b6aa21..7b5e3ed3a 100644 --- a/bot/cogs/tags.py +++ b/bot/cogs/tags.py @@ -1,5 +1,4 @@ import logging -import os import re import time from pathlib import Path @@ -39,18 +38,17 @@ class Tags(Cog): async def get_tags(self) -> None: """Get all tags.""" # Save all tags in memory. - tag_files = os.listdir("bot/resources/tags") + tag_files = Path("bot", "resources", "tags").iterdir() for file in tag_files: - p = Path("bot", "resources", "tags", file) - tag_title = os.path.splitext(file)[0].lower() - with p.open() as f: - tag = { - "title": tag_title, - "embed": { - "description": f.read() - } + file_path = Path(file) + tag_title = file_path.stem + tag = { + "title": tag_title, + "embed": { + "description": file_path.read_text() } - self._cache[tag_title] = tag + } + self._cache[tag_title] = tag @staticmethod def _fuzzy_search(search: str, target: str) -> float: -- cgit v1.2.3 From 073fdf1c69a9e4a2f9967d43a3e9960e9388aa23 Mon Sep 17 00:00:00 2001 From: RohanJnr Date: Wed, 4 Mar 2020 23:43:45 +0530 Subject: Convert "get_tags()" and "_get_tag()" to sync functions "get_tags()" and "_get_tag()" functions need not be async as we are no longer doing any API call but instead reading from local files. --- bot/cogs/tags.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/bot/cogs/tags.py b/bot/cogs/tags.py index 7b5e3ed3a..9665aa04e 100644 --- a/bot/cogs/tags.py +++ b/bot/cogs/tags.py @@ -28,16 +28,12 @@ class Tags(Cog): def __init__(self, bot: Bot): self.bot = bot self.tag_cooldowns = {} - self._cache = {} + self._cache = self.get_tags() - @Cog.listener() - async def on_ready(self) -> None: - """Runs the code before the bot has connected.""" - await self.get_tags() - - async def get_tags(self) -> None: + def get_tags(self) -> dict: """Get all tags.""" # Save all tags in memory. + cache = {} tag_files = Path("bot", "resources", "tags").iterdir() for file in tag_files: file_path = Path(file) @@ -48,7 +44,8 @@ class Tags(Cog): "description": file_path.read_text() } } - self._cache[tag_title] = tag + cache[tag_title] = tag + return cache @staticmethod def _fuzzy_search(search: str, target: str) -> float: @@ -87,7 +84,7 @@ class Tags(Cog): return [] - async def _get_tag(self, tag_name: str) -> list: + def _get_tag(self, tag_name: str) -> list: """Get a specific tag.""" found = [self._cache.get(tag_name.lower(), None)] if not found[0]: @@ -130,7 +127,7 @@ class Tags(Cog): return if tag_name is not None: - founds = await self._get_tag(tag_name) + founds = self._get_tag(tag_name) if len(founds) == 1: tag = founds[0] -- cgit v1.2.3 From 564690f79a61944c25d62530caaae671f6afcb31 Mon Sep 17 00:00:00 2001 From: "S. Co1" Date: Wed, 4 Mar 2020 17:31:11 -0500 Subject: Update tag files for new linting hooks --- bot/resources/tags/args-kwargs.md | 2 +- bot/resources/tags/ask.md | 2 +- bot/resources/tags/class.md | 4 ++-- bot/resources/tags/classmethod.md | 2 +- bot/resources/tags/codeblock.md | 2 +- bot/resources/tags/decorators.md | 6 +++--- bot/resources/tags/dictcomps.md | 2 +- bot/resources/tags/enumerate.md | 4 ++-- bot/resources/tags/except.md | 2 +- bot/resources/tags/exit().md | 2 +- bot/resources/tags/f-strings.md | 2 +- bot/resources/tags/foo.md | 2 +- bot/resources/tags/functions-are-objects.md | 2 +- bot/resources/tags/global.md | 2 +- bot/resources/tags/if-name-main.md | 2 +- bot/resources/tags/indent.md | 4 ++-- bot/resources/tags/inline.md | 2 +- bot/resources/tags/iterate-dict.md | 2 +- bot/resources/tags/listcomps.md | 2 +- bot/resources/tags/mutable-default-args.md | 6 +++--- bot/resources/tags/names.md | 2 +- bot/resources/tags/off-topic.md | 6 +++--- bot/resources/tags/open.md | 2 +- bot/resources/tags/or-gotcha.md | 2 +- bot/resources/tags/param-arg.md | 2 +- bot/resources/tags/paste.md | 2 +- bot/resources/tags/pathlib.md | 2 +- bot/resources/tags/pep8.md | 2 +- bot/resources/tags/positional-keyword.md | 4 ++-- bot/resources/tags/precedence.md | 2 +- bot/resources/tags/quotes.md | 2 +- bot/resources/tags/relative-path.md | 2 +- bot/resources/tags/repl.md | 2 +- bot/resources/tags/return.md | 6 +++--- bot/resources/tags/round.md | 2 +- bot/resources/tags/scope.md | 4 ++-- bot/resources/tags/seek.md | 2 +- bot/resources/tags/self.md | 2 +- bot/resources/tags/star-imports.md | 2 +- bot/resources/tags/traceback.md | 2 +- bot/resources/tags/windows-path.md | 4 ++-- bot/resources/tags/with.md | 2 +- bot/resources/tags/xy-problem.md | 2 +- bot/resources/tags/ytdl.md | 2 +- bot/resources/tags/zip.md | 2 +- 45 files changed, 59 insertions(+), 59 deletions(-) diff --git a/bot/resources/tags/args-kwargs.md b/bot/resources/tags/args-kwargs.md index de883dea8..b440a2346 100644 --- a/bot/resources/tags/args-kwargs.md +++ b/bot/resources/tags/args-kwargs.md @@ -14,4 +14,4 @@ These special parameters allow functions to take arbitrary amounts of positional • **Future proofing** (in the case of the first two bullet points, if the parameters change, your code won't break) • **Flexibility** (writing functions that behave like `dict()` or `print()`) -*See* `!tags positional-keyword` *for information about positional and keyword arguments* \ No newline at end of file +*See* `!tags positional-keyword` *for information about positional and keyword arguments* diff --git a/bot/resources/tags/ask.md b/bot/resources/tags/ask.md index ed651e8c5..e2c2a88f6 100644 --- a/bot/resources/tags/ask.md +++ b/bot/resources/tags/ask.md @@ -6,4 +6,4 @@ Asking good questions will yield a much higher chance of a quick response: • Show us the code you've tried and any errors or unexpected results it's giving. • Be patient while we're helping you. -You can find a much more detailed explanation [on our website](https://pythondiscord.com/pages/asking-good-questions/). \ No newline at end of file +You can find a much more detailed explanation [on our website](https://pythondiscord.com/pages/asking-good-questions/). diff --git a/bot/resources/tags/class.md b/bot/resources/tags/class.md index 74c36b9fa..4f73fc974 100644 --- a/bot/resources/tags/class.md +++ b/bot/resources/tags/class.md @@ -10,7 +10,7 @@ Here is an example class: class Foo: def __init__(self, somedata): self.my_attrib = somedata - + def show(self): print(self.my_attrib) ``` @@ -22,4 +22,4 @@ bar = Foo('data') bar.show() ``` -We can access any of `Foo`'s methods via `bar.my_method()`, and access any of `bar`s data via `bar.my_attribute`. \ No newline at end of file +We can access any of `Foo`'s methods via `bar.my_method()`, and access any of `bar`s data via `bar.my_attribute`. diff --git a/bot/resources/tags/classmethod.md b/bot/resources/tags/classmethod.md index 43c6d9909..a4e803093 100644 --- a/bot/resources/tags/classmethod.md +++ b/bot/resources/tags/classmethod.md @@ -17,4 +17,4 @@ alternative_bot = Bot.from_config(default_config) # but this still works, too regular_bot = Bot("tokenstring") ``` -This is just one of the many use cases of `@classmethod`. A more in-depth explanation can be found [here](https://stackoverflow.com/questions/12179271/meaning-of-classmethod-and-staticmethod-for-beginner#12179752). \ No newline at end of file +This is just one of the many use cases of `@classmethod`. A more in-depth explanation can be found [here](https://stackoverflow.com/questions/12179271/meaning-of-classmethod-and-staticmethod-for-beginner#12179752). diff --git a/bot/resources/tags/codeblock.md b/bot/resources/tags/codeblock.md index 34db060ef..a28ae397b 100644 --- a/bot/resources/tags/codeblock.md +++ b/bot/resources/tags/codeblock.md @@ -14,4 +14,4 @@ Note: This will result in the following: ```py print('Hello world!') -``` \ No newline at end of file +``` diff --git a/bot/resources/tags/decorators.md b/bot/resources/tags/decorators.md index 9b53af064..39c943f0a 100644 --- a/bot/resources/tags/decorators.md +++ b/bot/resources/tags/decorators.md @@ -12,12 +12,12 @@ Consider the following example of a timer decorator: ... print('Time elapsed:', time.time() - start) ... return result ... return inner -... +... >>> @timer ... def slow(delay=1): ... time.sleep(delay) ... return 'Finished!' -... +... >>> print(slow()) Time elapsed: 1.0011568069458008 Finished! @@ -28,4 +28,4 @@ Finished! More information: • [Corey Schafer's video on decorators](https://youtu.be/FsAPt_9Bf3U) -• [Real python article](https://realpython.com/primer-on-python-decorators/) \ No newline at end of file +• [Real python article](https://realpython.com/primer-on-python-decorators/) diff --git a/bot/resources/tags/dictcomps.md b/bot/resources/tags/dictcomps.md index ddefa1299..11867d77b 100644 --- a/bot/resources/tags/dictcomps.md +++ b/bot/resources/tags/dictcomps.md @@ -17,4 +17,4 @@ They are also very useful for inverting the key value pairs of a dictionary that Also like list comprehensions, you can add a conditional to it in order to filter out items you don't want. -For more information and examples, check [PEP 274](https://www.python.org/dev/peps/pep-0274/) \ No newline at end of file +For more information and examples, check [PEP 274](https://www.python.org/dev/peps/pep-0274/) diff --git a/bot/resources/tags/enumerate.md b/bot/resources/tags/enumerate.md index 610843cf4..dd984af52 100644 --- a/bot/resources/tags/enumerate.md +++ b/bot/resources/tags/enumerate.md @@ -4,10 +4,10 @@ index = 0 for item in my_list: print(f"{index}: {item}") index += 1 -``` +``` into beautiful, _pythonic_ code: ```py for index, item in enumerate(my_list): print(f"{index}: {item}") ``` -For more information, check out [the official docs](https://docs.python.org/3/library/functions.html#enumerate), or [PEP 279](https://www.python.org/dev/peps/pep-0279/). \ No newline at end of file +For more information, check out [the official docs](https://docs.python.org/3/library/functions.html#enumerate), or [PEP 279](https://www.python.org/dev/peps/pep-0279/). diff --git a/bot/resources/tags/except.md b/bot/resources/tags/except.md index 66dce13ab..8f0abf156 100644 --- a/bot/resources/tags/except.md +++ b/bot/resources/tags/except.md @@ -14,4 +14,4 @@ try: except: print("An exception was raised, but we have no idea if it was a ValueError or an IndexError.") ``` -For more information about exception handling, see [the official Python docs](https://docs.python.org/3/tutorial/errors.html), or watch [Corey Schafer's video on exception handling](https://www.youtube.com/watch?v=NIWwJbo-9_8). \ No newline at end of file +For more information about exception handling, see [the official Python docs](https://docs.python.org/3/tutorial/errors.html), or watch [Corey Schafer's video on exception handling](https://www.youtube.com/watch?v=NIWwJbo-9_8). diff --git a/bot/resources/tags/exit().md b/bot/resources/tags/exit().md index 89f83f7e0..27da9f866 100644 --- a/bot/resources/tags/exit().md +++ b/bot/resources/tags/exit().md @@ -5,4 +5,4 @@ If you want to exit your code programmatically, you might think to use the funct You should use either [`SystemExit`](https://docs.python.org/3/library/exceptions.html#SystemExit) or [`sys.exit()`](https://docs.python.org/3/library/sys.html#sys.exit) instead. There's not much practical difference between these two other than having to `import sys` for the latter. Both take an optional argument to provide an exit status. -[Official documentation](https://docs.python.org/3/library/constants.html#exit) with the warning not to use `exit()` or `quit()` in source code. \ No newline at end of file +[Official documentation](https://docs.python.org/3/library/constants.html#exit) with the warning not to use `exit()` or `quit()` in source code. diff --git a/bot/resources/tags/f-strings.md b/bot/resources/tags/f-strings.md index 966fe6080..69bc82487 100644 --- a/bot/resources/tags/f-strings.md +++ b/bot/resources/tags/f-strings.md @@ -14,4 +14,4 @@ print("{0} are some of the largest snakes in the world".format(snake)) # Or keyword arguments print("{family} are some of the largest snakes in the world".format(family=snake)) -``` \ No newline at end of file +``` diff --git a/bot/resources/tags/foo.md b/bot/resources/tags/foo.md index 2b5b659fd..98529bfc0 100644 --- a/bot/resources/tags/foo.md +++ b/bot/resources/tags/foo.md @@ -7,4 +7,4 @@ Python has its own metasyntactic variables, namely `spam`, `eggs`, and `bacon`. More information: • [History of foobar](https://en.wikipedia.org/wiki/Foobar) -• [Monty Python sketch](https://en.wikipedia.org/wiki/Spam_%28Monty_Python%29) \ No newline at end of file +• [Monty Python sketch](https://en.wikipedia.org/wiki/Spam_%28Monty_Python%29) diff --git a/bot/resources/tags/functions-are-objects.md b/bot/resources/tags/functions-are-objects.md index d10e6b73e..01af7a721 100644 --- a/bot/resources/tags/functions-are-objects.md +++ b/bot/resources/tags/functions-are-objects.md @@ -36,4 +36,4 @@ class C: # open function is passed # to the staticmethod class -``` \ No newline at end of file +``` diff --git a/bot/resources/tags/global.md b/bot/resources/tags/global.md index fc60f9177..64c316b62 100644 --- a/bot/resources/tags/global.md +++ b/bot/resources/tags/global.md @@ -13,4 +13,4 @@ def update_score(score, roll): return score + roll score = update_score(score, roll) ``` -For in-depth explanations on why global variables are bad news in a variety of situations, see [this Stack Overflow answer](https://stackoverflow.com/questions/19158339/why-are-global-variables-evil/19158418#19158418). \ No newline at end of file +For in-depth explanations on why global variables are bad news in a variety of situations, see [this Stack Overflow answer](https://stackoverflow.com/questions/19158339/why-are-global-variables-evil/19158418#19158418). diff --git a/bot/resources/tags/if-name-main.md b/bot/resources/tags/if-name-main.md index d44f0086d..9d88bb897 100644 --- a/bot/resources/tags/if-name-main.md +++ b/bot/resources/tags/if-name-main.md @@ -23,4 +23,4 @@ If you run this module named `bar.py`, it will execute the code in `foo.py`. Fir • Your module is a library, but also has a special case where it can be run directly • Your module is a library and you want to safeguard it against people running it directly (like what `pip` does) -• Your module is the main program, but has unit tests and the testing framework works by importing your module, and you want to avoid having your main code run during the test \ No newline at end of file +• Your module is the main program, but has unit tests and the testing framework works by importing your module, and you want to avoid having your main code run during the test diff --git a/bot/resources/tags/indent.md b/bot/resources/tags/indent.md index 5b36a4818..dec8407b0 100644 --- a/bot/resources/tags/indent.md +++ b/bot/resources/tags/indent.md @@ -12,7 +12,7 @@ def foo(): print('ham') # indented two levels return bar # indented one level ``` -The first line is not indented. The next two lines are indented to be inside of the function definition. They will only run when the function is called. The fourth line is indented to be inside the `if` statement, and will only run if the `if` statement evaluates to `True`. The fifth and last line is like the 2nd and 3rd and will always run when the function is called. It effectively closes the `if` statement above as no more lines can be inside the `if` statement below that line. +The first line is not indented. The next two lines are indented to be inside of the function definition. They will only run when the function is called. The fourth line is indented to be inside the `if` statement, and will only run if the `if` statement evaluates to `True`. The fifth and last line is like the 2nd and 3rd and will always run when the function is called. It effectively closes the `if` statement above as no more lines can be inside the `if` statement below that line. **Indentation is used after:** **1.** [Compound statements](https://docs.python.org/3/reference/compound_stmts.html) (eg. `if`, `while`, `for`, `try`, `with`, `def`, `class`, and their counterparts) @@ -21,4 +21,4 @@ The first line is not indented. The next two lines are indented to be inside of **More Info** **1.** [Indentation style guide](https://www.python.org/dev/peps/pep-0008/#indentation) **2.** [Tabs or Spaces?](https://www.python.org/dev/peps/pep-0008/#tabs-or-spaces) -**3.** [Official docs on indentation](https://docs.python.org/3/reference/lexical_analysis.html#indentation) \ No newline at end of file +**3.** [Official docs on indentation](https://docs.python.org/3/reference/lexical_analysis.html#indentation) diff --git a/bot/resources/tags/inline.md b/bot/resources/tags/inline.md index d0c9d1b5e..a6a7c35d6 100644 --- a/bot/resources/tags/inline.md +++ b/bot/resources/tags/inline.md @@ -13,4 +13,4 @@ The `__init__` method customizes the newly created instance. **Note:** • These are **backticks** not quotes • Avoid using them for multiple lines -• Useful for negating formatting you don't want \ No newline at end of file +• Useful for negating formatting you don't want diff --git a/bot/resources/tags/iterate-dict.md b/bot/resources/tags/iterate-dict.md index b23475506..78c067b20 100644 --- a/bot/resources/tags/iterate-dict.md +++ b/bot/resources/tags/iterate-dict.md @@ -7,4 +7,4 @@ To iterate over both the keys and values: ```py for key, val in my_dict.items(): print(key, val) -``` \ No newline at end of file +``` diff --git a/bot/resources/tags/listcomps.md b/bot/resources/tags/listcomps.md index 5ef0ce2bc..0003b9bb8 100644 --- a/bot/resources/tags/listcomps.md +++ b/bot/resources/tags/listcomps.md @@ -11,4 +11,4 @@ even_numbers = [n for n in range(20) if n % 2 == 0] ``` This also works for generators, dicts and sets by using `()` or `{}` instead of `[]`. -For more info, see [this pythonforbeginners.com post](http://www.pythonforbeginners.com/basics/list-comprehensions-in-python) or [PEP 202](https://www.python.org/dev/peps/pep-0202/). \ No newline at end of file +For more info, see [this pythonforbeginners.com post](http://www.pythonforbeginners.com/basics/list-comprehensions-in-python) or [PEP 202](https://www.python.org/dev/peps/pep-0202/). diff --git a/bot/resources/tags/mutable-default-args.md b/bot/resources/tags/mutable-default-args.md index 7b16e6b82..a8f0c38b3 100644 --- a/bot/resources/tags/mutable-default-args.md +++ b/bot/resources/tags/mutable-default-args.md @@ -11,7 +11,7 @@ and returns it. `foo` is set to an empty list by default. >>> def append_one(foo=[]): ... foo.append(1) ... return foo -... +... ``` See what happens when we call it a few times: ```python @@ -33,7 +33,7 @@ function is **called**: ... foo = [] ... foo.append(1) ... return foo -... +... >>> append_one() [1] >>> append_one() @@ -45,4 +45,4 @@ function is **called**: • This behavior can be used intentionally to maintain state between calls of a function (eg. when writing a caching function). • This behavior is not unique to mutable objects, all default -arguments are evaulated only once when the function is defined. \ No newline at end of file +arguments are evaulated only once when the function is defined. diff --git a/bot/resources/tags/names.md b/bot/resources/tags/names.md index 462c550bc..3e76269f7 100644 --- a/bot/resources/tags/names.md +++ b/bot/resources/tags/names.md @@ -34,4 +34,4 @@ There is also `del` which has the purpose of *unbinding* a name. **More info** • Please watch [Ned Batchelder's talk](https://youtu.be/_AEJHKGk9ns) on names in python for a detailed explanation with examples -• [Official documentation](https://docs.python.org/3/reference/executionmodel.html#naming-and-binding) \ No newline at end of file +• [Official documentation](https://docs.python.org/3/reference/executionmodel.html#naming-and-binding) diff --git a/bot/resources/tags/off-topic.md b/bot/resources/tags/off-topic.md index 004adfa17..c7f98a813 100644 --- a/bot/resources/tags/off-topic.md +++ b/bot/resources/tags/off-topic.md @@ -1,8 +1,8 @@ **Off-topic channels** -There are three off-topic channels: -• <#291284109232308226> +There are three off-topic channels: +• <#291284109232308226> • <#463035241142026251> • <#463035268514185226> -Their names change randomly every 24 hours, but you can always find them under the `OFF-TOPIC/GENERAL` category in the channel list. \ No newline at end of file +Their names change randomly every 24 hours, but you can always find them under the `OFF-TOPIC/GENERAL` category in the channel list. diff --git a/bot/resources/tags/open.md b/bot/resources/tags/open.md index 1ba19dedd..13b4555b9 100644 --- a/bot/resources/tags/open.md +++ b/bot/resources/tags/open.md @@ -23,4 +23,4 @@ This is an optional string that specifies the mode in which the file should be o `'w+'` Opens for reading and writing and truncates (can create files) `'x'` Creates file and opens for writing (file must **not** already exist) `'x+'` Creates file and opens for reading and writing (file must **not** already exist) -`'a+'` Opens file for reading and writing at **end of file** (can create files) \ No newline at end of file +`'a+'` Opens file for reading and writing at **end of file** (can create files) diff --git a/bot/resources/tags/or-gotcha.md b/bot/resources/tags/or-gotcha.md index da82e3fdd..00c2db1f8 100644 --- a/bot/resources/tags/or-gotcha.md +++ b/bot/resources/tags/or-gotcha.md @@ -14,4 +14,4 @@ if favorite_fruit == 'grapefruit' or favorite_fruit == 'lemon': # ...or like this. if favorite_fruit in ('grapefruit', 'lemon'): print("That's a weird favorite fruit to have.") -``` \ No newline at end of file +``` diff --git a/bot/resources/tags/param-arg.md b/bot/resources/tags/param-arg.md index 9e946812b..88069d8bd 100644 --- a/bot/resources/tags/param-arg.md +++ b/bot/resources/tags/param-arg.md @@ -9,4 +9,4 @@ def square(n): # n is the parameter print(square(5)) # 5 is the argument ``` -Note that `5` is the argument passed to `square`, but `square(5)` in its entirety is the argument passed to `print` \ No newline at end of file +Note that `5` is the argument passed to `square`, but `square(5)` in its entirety is the argument passed to `print` diff --git a/bot/resources/tags/paste.md b/bot/resources/tags/paste.md index d8e6e6c61..2ed51def7 100644 --- a/bot/resources/tags/paste.md +++ b/bot/resources/tags/paste.md @@ -3,4 +3,4 @@ If your code is too long to fit in a codeblock in discord, you can paste your code here: https://paste.pydis.com/ -After pasting your code, **save** it by clicking the floppy disk icon in the top right, or by typing `ctrl + S`. After doing that, the URL should **change**. Copy the URL and post it here so others can see it. \ No newline at end of file +After pasting your code, **save** it by clicking the floppy disk icon in the top right, or by typing `ctrl + S`. After doing that, the URL should **change**. Copy the URL and post it here so others can see it. diff --git a/bot/resources/tags/pathlib.md b/bot/resources/tags/pathlib.md index 468945cc5..dfeb7ecac 100644 --- a/bot/resources/tags/pathlib.md +++ b/bot/resources/tags/pathlib.md @@ -18,4 +18,4 @@ Python 3 comes with a new module named `Pathlib`. Since Python 3.6, `pathlib.Pat • [**Why you should use pathlib** - Trey Hunner](https://treyhunner.com/2018/12/why-you-should-be-using-pathlib/) • [**Answering concerns about pathlib** - Trey Hunner](https://treyhunner.com/2019/01/no-really-pathlib-is-great/) • [**Official Documentation**](https://docs.python.org/3/library/pathlib.html) -• [**PEP 519** - Adding a file system path protocol](https://www.python.org/dev/peps/pep-0519/) \ No newline at end of file +• [**PEP 519** - Adding a file system path protocol](https://www.python.org/dev/peps/pep-0519/) diff --git a/bot/resources/tags/pep8.md b/bot/resources/tags/pep8.md index ec999bedc..cab4c4db8 100644 --- a/bot/resources/tags/pep8.md +++ b/bot/resources/tags/pep8.md @@ -1,3 +1,3 @@ **PEP 8** is the official style guide for Python. It includes comprehensive guidelines for code formatting, variable naming, and making your code easy to read. Professional Python developers are usually required to follow the guidelines, and will often use code-linters like `flake8` to verify that the code they\'re writing complies with the style guide. -You can find the PEP 8 document [here](https://www.python.org/dev/peps/pep-0008). \ No newline at end of file +You can find the PEP 8 document [here](https://www.python.org/dev/peps/pep-0008). diff --git a/bot/resources/tags/positional-keyword.md b/bot/resources/tags/positional-keyword.md index bc7f68ee0..dd6ddfc4b 100644 --- a/bot/resources/tags/positional-keyword.md +++ b/bot/resources/tags/positional-keyword.md @@ -25,7 +25,7 @@ The reverse is also true: ```py >>> def foo(a, b): ... print(a, b) -... +... >>> foo(a=1, b=2) 1 2 >>> foo(b=1, a=2) @@ -35,4 +35,4 @@ The reverse is also true: **More info** • [Keyword only arguments](https://www.python.org/dev/peps/pep-3102/) • [Positional only arguments](https://www.python.org/dev/peps/pep-0570/) -• `!tags param-arg` (Parameters vs. Arguments) \ No newline at end of file +• `!tags param-arg` (Parameters vs. Arguments) diff --git a/bot/resources/tags/precedence.md b/bot/resources/tags/precedence.md index 8a4c66c4e..ed399143c 100644 --- a/bot/resources/tags/precedence.md +++ b/bot/resources/tags/precedence.md @@ -10,4 +10,4 @@ Operator precedence is essentially like an order of operations for python's oper `not True or True` is `True` because the `not` is first `not (True or True)` is `False` because the `or` is first -The full table of precedence from lowest to highest is [here](https://docs.python.org/3/reference/expressions.html#operator-precedence) \ No newline at end of file +The full table of precedence from lowest to highest is [here](https://docs.python.org/3/reference/expressions.html#operator-precedence) diff --git a/bot/resources/tags/quotes.md b/bot/resources/tags/quotes.md index bb6e2a009..8421748a1 100644 --- a/bot/resources/tags/quotes.md +++ b/bot/resources/tags/quotes.md @@ -17,4 +17,4 @@ If you need both single and double quotes inside your string, use the version th **References:** • [pep-8 on quotes](https://www.python.org/dev/peps/pep-0008/#string-quotes) -• [convention for triple quoted strings](https://www.python.org/dev/peps/pep-0257/) \ No newline at end of file +• [convention for triple quoted strings](https://www.python.org/dev/peps/pep-0257/) diff --git a/bot/resources/tags/relative-path.md b/bot/resources/tags/relative-path.md index 269276e81..6e97b78af 100644 --- a/bot/resources/tags/relative-path.md +++ b/bot/resources/tags/relative-path.md @@ -4,4 +4,4 @@ A relative path is a partial path that is relative to your current working direc **Why is this important?** -When opening files in python, relative paths won't always work since it's dependent on what directory you were in when you ran your code. A common issue people face is running their code in an IDE thinking they can open files that are in the same directory as their module, but the current working directory will be different than what they expect and so they won't find the file. The way to avoid this problem is by using absolute paths, which is the full path from your root directory to the file you want to open. \ No newline at end of file +When opening files in python, relative paths won't always work since it's dependent on what directory you were in when you ran your code. A common issue people face is running their code in an IDE thinking they can open files that are in the same directory as their module, but the current working directory will be different than what they expect and so they won't find the file. The way to avoid this problem is by using absolute paths, which is the full path from your root directory to the file you want to open. diff --git a/bot/resources/tags/repl.md b/bot/resources/tags/repl.md index a68fe9397..875b4ec47 100644 --- a/bot/resources/tags/repl.md +++ b/bot/resources/tags/repl.md @@ -10,4 +10,4 @@ Alternatively, you can make use of the builtin `help()` function. `help(thing)` Lastly you can run your code with the `-i` flag to execute your code normally, but be dropped into the REPL once execution is finished, giving you access to all your global variables/functions in the REPL. -To **exit** either a help session, or normal REPL prompt, you must send an EOF signal to the prompt. In *nix systems, this is done with `ctrl + D`, and in windows systems it is `ctrl + Z`. You can also exit the normal REPL prompt with the dedicated functions `exit()` or `quit()`. \ No newline at end of file +To **exit** either a help session, or normal REPL prompt, you must send an EOF signal to the prompt. In *nix systems, this is done with `ctrl + D`, and in windows systems it is `ctrl + Z`. You can also exit the normal REPL prompt with the dedicated functions `exit()` or `quit()`. diff --git a/bot/resources/tags/return.md b/bot/resources/tags/return.md index c944dddf2..e37f0eebc 100644 --- a/bot/resources/tags/return.md +++ b/bot/resources/tags/return.md @@ -16,13 +16,13 @@ If we wanted to store 5 squared in a variable called `x`, we could do that like ```py >>> def square(n): ... n*n # calculates then throws away, returns None -... +... >>> x = square(5) >>> print(x) None >>> def square(n): ... print(n*n) # calculates and prints, then throws away and returns None -... +... >>> x = square(5) 25 >>> print(x) @@ -32,4 +32,4 @@ None • `print()` and `return` do **not** accomplish the same thing. `print()` will only print the value, it will not be accessible outside of the function afterwards. • A function will return `None` if it ends without reaching an explicit `return` statement. • When you want to print a value calculated in a function, instead of printing inside the function, it is often better to return the value and print the *function call* instead. -• [Official documentation for `return`](https://docs.python.org/3/reference/simple_stmts.html#the-return-statement) \ No newline at end of file +• [Official documentation for `return`](https://docs.python.org/3/reference/simple_stmts.html#the-return-statement) diff --git a/bot/resources/tags/round.md b/bot/resources/tags/round.md index 28a12469a..0392bb41b 100644 --- a/bot/resources/tags/round.md +++ b/bot/resources/tags/round.md @@ -21,4 +21,4 @@ It should be noted that round half to even distorts the distribution by increasi • [Wikipedia article about rounding](https://en.wikipedia.org/wiki/Rounding#Round_half_to_even) • [Documentation on `round` function](https://docs.python.org/3/library/functions.html#round) • [`round` in what's new in python 3](https://docs.python.org/3/whatsnew/3.0.html#builtins) (4th bullet down) -• [How to force rounding technique](https://stackoverflow.com/a/10826537/4607272) \ No newline at end of file +• [How to force rounding technique](https://stackoverflow.com/a/10826537/4607272) diff --git a/bot/resources/tags/scope.md b/bot/resources/tags/scope.md index c1eeb3b84..5c1e64e1c 100644 --- a/bot/resources/tags/scope.md +++ b/bot/resources/tags/scope.md @@ -13,7 +13,7 @@ Alternatively if a variable is defined within a function block for example, it i ... def inner(): ... print(foo) # has access to foo from scope of outer ... return inner # brings inner to scope of caller -... +... >>> inner = outer() # get inner function >>> inner() # prints variable foo without issue bar @@ -21,4 +21,4 @@ bar **Official Documentation** **1.** [Program structure, name binding and resolution](https://docs.python.org/3/reference/executionmodel.html#execution-model) **2.** [`global` statement](https://docs.python.org/3/reference/simple_stmts.html#the-global-statement) -**3.** [`nonlocal` statement](https://docs.python.org/3/reference/simple_stmts.html#the-nonlocal-statement) \ No newline at end of file +**3.** [`nonlocal` statement](https://docs.python.org/3/reference/simple_stmts.html#the-nonlocal-statement) diff --git a/bot/resources/tags/seek.md b/bot/resources/tags/seek.md index ff6569a0c..bc013fe03 100644 --- a/bot/resources/tags/seek.md +++ b/bot/resources/tags/seek.md @@ -19,4 +19,4 @@ Finally, lets do `f.seek(-4, 2)`, moving our stream position *backwards* 4 bytes **Note** • For the second argument in `seek()`, use `os.SEEK_SET`, `os.SEEK_CUR`, and `os.SEEK_END` in place of 0, 1, and 2 respectively. -• `os.SEEK_CUR` is only usable when the file is in byte mode. \ No newline at end of file +• `os.SEEK_CUR` is only usable when the file is in byte mode. diff --git a/bot/resources/tags/self.md b/bot/resources/tags/self.md index a9cd5e9df..d20154fd5 100644 --- a/bot/resources/tags/self.md +++ b/bot/resources/tags/self.md @@ -22,4 +22,4 @@ doing `Foo.spam(foo, 'ham')`. Methods do not inherently have access to attributes defined in the class. In order for any one method to be able to access other methods or variables defined in the class, it must have access to the instance. -Consider if outside the class, we tried to do this: `spam(foo, 'ham')`. This would give an error, because we don't have access to the `spam` method directly, we have to call it by doing `foo.spam('ham')`. This is also the case inside of the class. If we wanted to call the `bar` method inside the `spam` method, we'd have to do `self.bar()`, just doing `bar()` would give an error. \ No newline at end of file +Consider if outside the class, we tried to do this: `spam(foo, 'ham')`. This would give an error, because we don't have access to the `spam` method directly, we have to call it by doing `foo.spam('ham')`. This is also the case inside of the class. If we wanted to call the `bar` method inside the `spam` method, we'd have to do `self.bar()`, just doing `bar()` would give an error. diff --git a/bot/resources/tags/star-imports.md b/bot/resources/tags/star-imports.md index 4c7e0199c..2be6aab6e 100644 --- a/bot/resources/tags/star-imports.md +++ b/bot/resources/tags/star-imports.md @@ -45,4 +45,4 @@ Conclusion: Namespaces are one honking great idea -- let's do more of those! *[3 **[2]** [Namespaces and scopes](https://www.programiz.com/python-programming/namespace) -**[3]** [Zen of Python](https://www.python.org/dev/peps/pep-0020/) \ No newline at end of file +**[3]** [Zen of Python](https://www.python.org/dev/peps/pep-0020/) diff --git a/bot/resources/tags/traceback.md b/bot/resources/tags/traceback.md index 678ba1991..46ef40aa1 100644 --- a/bot/resources/tags/traceback.md +++ b/bot/resources/tags/traceback.md @@ -15,4 +15,4 @@ The best way to read your traceback is bottom to top. • Make note of the line number, and navigate there in your program. • Try to understand why the error occurred. -To read more about exceptions and errors, please refer to the [PyDis Wiki](https://pythondiscord.com/pages/asking-good-questions/#examining-tracebacks) or the [official Python tutorial.](https://docs.python.org/3.7/tutorial/errors.html) \ No newline at end of file +To read more about exceptions and errors, please refer to the [PyDis Wiki](https://pythondiscord.com/pages/asking-good-questions/#examining-tracebacks) or the [official Python tutorial.](https://docs.python.org/3.7/tutorial/errors.html) diff --git a/bot/resources/tags/windows-path.md b/bot/resources/tags/windows-path.md index d8723f06f..da8edf685 100644 --- a/bot/resources/tags/windows-path.md +++ b/bot/resources/tags/windows-path.md @@ -1,6 +1,6 @@ **PATH on Windows** -If you have installed Python but you forgot to check the *Add Python to PATH* option during the installation you may still be able to access your installation with ease. +If you have installed Python but you forgot to check the *Add Python to PATH* option during the installation you may still be able to access your installation with ease. If you did not uncheck the option to install the Python launcher then you will find a `py` command on your system. If you want to be able to open your Python installation by running `python` then your best option is to re-install Python. @@ -27,4 +27,4 @@ C:\Users\Username> py -3.6 ... Python 3.6 stars ... C:\Users\Username> py -2 ... Python 2 (any version installed) starts ... -``` \ No newline at end of file +``` diff --git a/bot/resources/tags/with.md b/bot/resources/tags/with.md index a79eb7dbb..62d5612f2 100644 --- a/bot/resources/tags/with.md +++ b/bot/resources/tags/with.md @@ -5,4 +5,4 @@ with open("test.txt", "r") as file: ``` The above code automatically closes `file` when the `with` block exits, so you never have to manually do a `file.close()`. Most connection types, including file readers and database connections, support this. -For more information, read [the official docs](https://docs.python.org/3/reference/compound_stmts.html#with), watch [Corey Schafer\'s context manager video](https://www.youtube.com/watch?v=-aKFBoZpiqA), or see [PEP 343](https://www.python.org/dev/peps/pep-0343/). \ No newline at end of file +For more information, read [the official docs](https://docs.python.org/3/reference/compound_stmts.html#with), watch [Corey Schafer\'s context manager video](https://www.youtube.com/watch?v=-aKFBoZpiqA), or see [PEP 343](https://www.python.org/dev/peps/pep-0343/). diff --git a/bot/resources/tags/xy-problem.md b/bot/resources/tags/xy-problem.md index 77700e7a0..b77bd27e8 100644 --- a/bot/resources/tags/xy-problem.md +++ b/bot/resources/tags/xy-problem.md @@ -4,4 +4,4 @@ Asking about your attempted solution rather than your actual problem. Often programmers will get distracted with a potential solution they've come up with, and will try asking for help getting it to work. However, it's possible this solution either wouldn't work as they expect, or there's a much better solution instead. -For more information and examples: http://xyproblem.info/ \ No newline at end of file +For more information and examples: http://xyproblem.info/ diff --git a/bot/resources/tags/ytdl.md b/bot/resources/tags/ytdl.md index e1085d1af..09664af26 100644 --- a/bot/resources/tags/ytdl.md +++ b/bot/resources/tags/ytdl.md @@ -6,4 +6,4 @@ For reference, this usage is covered by the following clauses in [YouTube's TOS] ``` ``` 4C: You agree not to access Content through any technology or means other than the video playback pages of the Service itself, the Embeddable Player, or other explicitly authorized means YouTube may designate. -``` \ No newline at end of file +``` diff --git a/bot/resources/tags/zip.md b/bot/resources/tags/zip.md index 9d2fe5ee3..6b05f0282 100644 --- a/bot/resources/tags/zip.md +++ b/bot/resources/tags/zip.md @@ -9,4 +9,4 @@ for letter, number in zip(letters, numbers): ``` The `zip()` iterator is exhausted after the length of the shortest iterable is exceeded. If you would like to retain the other values, consider using [itertools.zip_longest](https://docs.python.org/3/library/itertools.html#itertools.zip_longest). -For more information on zip, please refer to the [official documentation](https://docs.python.org/3/library/functions.html#zip). \ No newline at end of file +For more information on zip, please refer to the [official documentation](https://docs.python.org/3/library/functions.html#zip). -- cgit v1.2.3 From 4662cfd29cd9c5ac0081621648a87d102b825852 Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Thu, 12 Mar 2020 15:05:41 +0100 Subject: Update ytdl tag to the new YouTube ToS --- bot/resources/tags/ytdl.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/bot/resources/tags/ytdl.md b/bot/resources/tags/ytdl.md index 09664af26..4c47b0595 100644 --- a/bot/resources/tags/ytdl.md +++ b/bot/resources/tags/ytdl.md @@ -1,9 +1,8 @@ Per [PyDis' Rule 5](https://pythondiscord.com/pages/rules), we are unable to assist with questions related to youtube-dl, commonly used by Discord bots to stream audio, as its use violates YouTube's Terms of Service. -For reference, this usage is covered by the following clauses in [YouTube's TOS](https://www.youtube.com/static?template=terms), as of 2018-05-25: +For reference, this usage is covered by the following clauses in [YouTube's TOS](https://www.youtube.com/static?template=terms), as of 2019-07-22: ``` -4A: You agree not to distribute in any medium any part of the Service or the Content without YouTube's prior written authorization, unless YouTube makes available the means for such distribution through functionality offered by the Service (such as the Embeddable Player). -``` -``` -4C: You agree not to access Content through any technology or means other than the video playback pages of the Service itself, the Embeddable Player, or other explicitly authorized means YouTube may designate. +The following restrictions apply to your use of the Service. You are not allowed to: + +3. access the Service using any automated means (such as robots, botnets or scrapers) except: (a) in the case of public search engines, in accordance with YouTube’s robots.txt file; (b) with YouTube’s prior written permission; or (c) as permitted by applicable law; ``` -- cgit v1.2.3 From f2d10e46e44b4d4cdd3dc343a2462ba00d654409 Mon Sep 17 00:00:00 2001 From: RohanJnr Date: Thu, 12 Mar 2020 22:18:24 +0530 Subject: remove repetitive file search --- bot/cogs/tags.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/bot/cogs/tags.py b/bot/cogs/tags.py index 9665aa04e..692cff0d8 100644 --- a/bot/cogs/tags.py +++ b/bot/cogs/tags.py @@ -36,12 +36,11 @@ class Tags(Cog): cache = {} tag_files = Path("bot", "resources", "tags").iterdir() for file in tag_files: - file_path = Path(file) - tag_title = file_path.stem + tag_title = file.stem tag = { "title": tag_title, "embed": { - "description": file_path.read_text() + "description": file.read_text() } } cache[tag_title] = tag -- cgit v1.2.3 From 55effb33627fe21a4bcd230a26cc3cf2dfb0b512 Mon Sep 17 00:00:00 2001 From: RohanJnr Date: Thu, 12 Mar 2020 22:32:24 +0530 Subject: convert get_tags() method to staticmethod --- bot/cogs/tags.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/cogs/tags.py b/bot/cogs/tags.py index 692cff0d8..48f000143 100644 --- a/bot/cogs/tags.py +++ b/bot/cogs/tags.py @@ -30,7 +30,8 @@ class Tags(Cog): self.tag_cooldowns = {} self._cache = self.get_tags() - def get_tags(self) -> dict: + @staticmethod + def get_tags() -> dict: """Get all tags.""" # Save all tags in memory. cache = {} -- cgit v1.2.3 From e9ae8a89ec97c3bdb53dd354e292c53ad72fb42e Mon Sep 17 00:00:00 2001 From: RohanJnr Date: Sat, 14 Mar 2020 19:43:31 +0530 Subject: Remove line that calls get_tags() method The tags have now been shifted from the database to being static files and hence the get_tags() method has undergone changes. It now dosen't fetch from the database but looks at the local files and we need not call it more than once. --- bot/cogs/tags.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/bot/cogs/tags.py b/bot/cogs/tags.py index fecaf926d..fee24b2e7 100644 --- a/bot/cogs/tags.py +++ b/bot/cogs/tags.py @@ -97,8 +97,6 @@ class Tags(Cog): `predicate` will be the built-in any, all, or a custom callable. Must return a bool. """ - await self._get_tags() - keywords_processed: List[str] = [] for keyword in keywords.split(','): keyword_sanitized = keyword.strip().casefold() -- cgit v1.2.3 From 52ed9aa590a4c190f70778448ec926df4f2d0119 Mon Sep 17 00:00:00 2001 From: MarkKoz Date: Sat, 14 Mar 2020 12:35:24 -0700 Subject: Tags: use constant for command prefix in embed footer * Add a constant for the footer text * Import constants module rather than its classes --- bot/cogs/tags.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/bot/cogs/tags.py b/bot/cogs/tags.py index fee24b2e7..09ce5a413 100644 --- a/bot/cogs/tags.py +++ b/bot/cogs/tags.py @@ -7,19 +7,20 @@ from typing import Callable, Dict, Iterable, List, Optional from discord import Colour, Embed from discord.ext.commands import Cog, Context, group +from bot import constants from bot.bot import Bot -from bot.constants import Channels, Cooldowns from bot.converters import TagNameConverter from bot.pagination import LinePaginator log = logging.getLogger(__name__) TEST_CHANNELS = ( - Channels.bot_commands, - Channels.helpers + constants.Channels.bot_commands, + constants.Channels.helpers ) REGEX_NON_ALPHABET = re.compile(r"[^a-z]", re.MULTILINE & re.IGNORECASE) +FOOTER_TEXT = f"To show a tag, type {constants.Bot.prefix}tags ." class Tags(Cog): @@ -133,7 +134,7 @@ class Tags(Cog): sorted(f"**»** {tag['title']}" for tag in matching_tags), ctx, embed, - footer_text="To show a tag, type !tags .", + footer_text=FOOTER_TEXT, empty=False, max_lines=15 ) @@ -177,7 +178,7 @@ class Tags(Cog): cooldown_conditions = ( tag_name and tag_name in self.tag_cooldowns - and (now - self.tag_cooldowns[tag_name]["time"]) < Cooldowns.tags + and (now - self.tag_cooldowns[tag_name]["time"]) < constants.Cooldowns.tags and self.tag_cooldowns[tag_name]["channel"] == ctx.channel.id ) @@ -186,7 +187,8 @@ class Tags(Cog): return False if _command_on_cooldown(tag_name): - time_left = Cooldowns.tags - (time.time() - self.tag_cooldowns[tag_name]["time"]) + time_elapsed = time.time() - self.tag_cooldowns[tag_name]["time"] + time_left = constants.Cooldowns.tags - time_elapsed log.info( f"{ctx.author} tried to get the '{tag_name}' tag, but the tag is on cooldown. " f"Cooldown ends in {time_left:.1f} seconds." @@ -223,7 +225,7 @@ class Tags(Cog): sorted(f"**»** {tag['title']}" for tag in tags), ctx, embed, - footer_text="To show a tag, type !tags .", + footer_text=FOOTER_TEXT, empty=False, max_lines=15 ) -- cgit v1.2.3 From eeacceae01b95e39eaeecab2fd14f6edfb19b94b Mon Sep 17 00:00:00 2001 From: MarkKoz Date: Sat, 14 Mar 2020 12:43:49 -0700 Subject: Tags: add restrictions 1 & 9 from YouTube ToS to ytdl tag --- bot/resources/tags/ytdl.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/bot/resources/tags/ytdl.md b/bot/resources/tags/ytdl.md index 4c47b0595..e34ecff44 100644 --- a/bot/resources/tags/ytdl.md +++ b/bot/resources/tags/ytdl.md @@ -2,7 +2,11 @@ Per [PyDis' Rule 5](https://pythondiscord.com/pages/rules), we are unable to ass For reference, this usage is covered by the following clauses in [YouTube's TOS](https://www.youtube.com/static?template=terms), as of 2019-07-22: ``` -The following restrictions apply to your use of the Service. You are not allowed to: +The following restrictions apply to your use of the Service. You are not allowed to: -3. access the Service using any automated means (such as robots, botnets or scrapers) except: (a) in the case of public search engines, in accordance with YouTube’s robots.txt file; (b) with YouTube’s prior written permission; or (c) as permitted by applicable law; +1. access, reproduce, download, distribute, transmit, broadcast, display, sell, license, alter, modify or otherwise use any part of the Service or any Content except: (a) as specifically permitted by the Service; (b) with prior written permission from YouTube and, if applicable, the respective rights holders; or (c) as permitted by applicable law; + +3. access the Service using any automated means (such as robots, botnets or scrapers) except: (a) in the case of public search engines, in accordance with YouTube’s robots.txt file; (b) with YouTube’s prior written permission; or (c) as permitted by applicable law; + +9. use the Service to view or listen to Content other than for personal, non-commercial use (for example, you may not publicly screen videos or stream music from the Service) ``` -- cgit v1.2.3 From 5865126b87aad1a9fd425f9b131d8520c82a96a5 Mon Sep 17 00:00:00 2001 From: RohanJnr Date: Sun, 15 Mar 2020 17:05:55 +0530 Subject: convert _get_tags_via_content() method to non-async --- bot/cogs/tags.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/cogs/tags.py b/bot/cogs/tags.py index fee24b2e7..ff3be7f4a 100644 --- a/bot/cogs/tags.py +++ b/bot/cogs/tags.py @@ -91,7 +91,7 @@ class Tags(Cog): return self._get_suggestions(tag_name) return found - async def _get_tags_via_content(self, check: Callable[[Iterable], bool], keywords: str) -> list: + def _get_tags_via_content(self, check: Callable[[Iterable], bool], keywords: str) -> list: """ Search for tags via contents. -- cgit v1.2.3 From 520c73093af8337c34fb7147ba7d7d15bf46a2af Mon Sep 17 00:00:00 2001 From: RohanJnr Date: Sun, 15 Mar 2020 18:37:37 +0530 Subject: not awaiting _get_tags_via_content() method as it is non-async --- bot/cogs/tags.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/cogs/tags.py b/bot/cogs/tags.py index 4895bd807..5b820978d 100644 --- a/bot/cogs/tags.py +++ b/bot/cogs/tags.py @@ -151,7 +151,7 @@ class Tags(Cog): Only search for tags that has ALL the keywords. """ - matching_tags = await self._get_tags_via_content(all, keywords) + matching_tags = self._get_tags_via_content(all, keywords) await self._send_matching_tags(ctx, keywords, matching_tags) @search_tag_content.command(name='any') @@ -161,7 +161,7 @@ class Tags(Cog): Search for tags that has ANY of the keywords. """ - matching_tags = await self._get_tags_via_content(any, keywords or 'any') + matching_tags = self._get_tags_via_content(any, keywords or 'any') await self._send_matching_tags(ctx, keywords, matching_tags) @tags_group.command(name='get', aliases=('show', 'g')) -- cgit v1.2.3 From 8eea9c39261d77f86181600164920a635edd3570 Mon Sep 17 00:00:00 2001 From: Shirayuki Nekomata Date: Mon, 16 Mar 2020 00:27:18 +0700 Subject: Fixed tag search via contents, any keywords. Fixed `!tag search any` raises `AttributeError`. Changed default value of `keywords` from `None` to `'any'`. This will make it search for keyword `'any'` when there is no keyword. --- bot/cogs/tags.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/cogs/tags.py b/bot/cogs/tags.py index 5b820978d..539105017 100644 --- a/bot/cogs/tags.py +++ b/bot/cogs/tags.py @@ -155,7 +155,7 @@ class Tags(Cog): await self._send_matching_tags(ctx, keywords, matching_tags) @search_tag_content.command(name='any') - async def search_tag_content_any_keyword(self, ctx: Context, *, keywords: Optional[str] = None) -> None: + async def search_tag_content_any_keyword(self, ctx: Context, *, keywords: Optional[str] = 'any') -> None: """ Search inside tags' contents for tags. Allow searching for multiple keywords separated by comma. -- cgit v1.2.3