#dev-contrib

1 messages Β· Page 130 of 1

thorny obsidian
#

yup! That's how it works. When you make a new branch you're essentially saying "from this point on, I want to make a new feature, but not prevent other development of the main branch, so I'll do it in a different area". Having the same commits there to maintain commit history is important

vale ibex
#

Yea, the way to do it is to make a branch, do some changes, then go back to main and branch from there again

#

rather than branching from your branch

desert vessel
#

A new branch is basically a copy of your existing branch where you can add stuff like one branch can be for dev and the other can be for prod

dim pelican
#

I have a fork of the repo, and my main branch has a few changes like more things in the .gitignore, .gitmessage etc. I want to keep those in my main branch of the fork, but when I make a new branch to work on an issue / PR it pulls from my forked main. I've learned I have to force pull / over write the new branch from the upstream main once I create it

thorny obsidian
#

aaah. Hmmmm, for that, if I want to start a branch at a later commit I usually use a GUI since it lets me select a commit and do a "start branch from here" option

#

Chris might know something better

dim pelican
green oriole
#

You'd want to checkout upstream/main and make a branch

#
git checkout upstream/main
git checkout -b $name
dim pelican
#

Ahh instead of making a branch on my fork

#

I don't think I can make a new branch on the upstream though

green oriole
#

Basically the branch you have checked out will be the forking point of your branch, if you following the tree analogy

green oriole
#

Checking out upstream/main means "go to the commit that the branch main on the upstream points to"

dim pelican
#

Gotcha, it just uses the upstream main as it's base "reference". Then the new branch inherits everything from there, and when I link a PR I point it to that branch of the upstream / origin?

green oriole
#

Yeah, you will make a PR from the newly created branch

dim pelican
#

I guess the only thing I'm not following is will that new branch be on my fork (brad90four/sir-lancebot) or the upstream (python-discord/sir-lancebot)

green oriole
#

When you create a new branch locally, it doesn't have any knowledge of which repo it is pointing to

#

When you first push, you link it to the remote branch

dim pelican
#

You should have heard a loud "click" just then

green oriole
#

git push -u origin $name will link it to the branch named $name on the origin remote

#

Haha, nice

dim pelican
#

Damn, just when I thought I sorta knew my way around git

fervent sage
#

you never know your way around git

#

you can be a world expert in git and still not know

green oriole
#

Haha, git is a complex topic. Not even Linus knows his way around it.

dim pelican
#
git fetch
git checkout upstream/main
git checkout -b <new_branch>
... # code
git add <changes>
git commit
git push -u origin <new_branch>

For my future reference, gonna bm this

green oriole
#

I would recommend adding a git fetch at the top

#

So you know upstream/main is up to date

dim pelican
#

Thank you, gotta keep practicing those git muscles

#

In the meantime, if anyone is interested:

#

sir-lancebot#833

dim pelican
#

sir-lancebot#834

fervent sage
#

time for me to practise my lancebot setup muscles

#

see how many PRs i can thoroughly review in a day KEKW

dim pelican
#

Oh scheiße my PR is borked @fervent sage

#

I hope that recent commit works

vale ibex
#

lmfao

#

trusting my in-git suggestion

green oriole
#

lol, nice

vale ibex
#

is that a force push I hear in the distance

desert vessel
vale ibex
#

Maybe add the last commit as a fixup to f31d196

#

if you're gonna force push anyway

desert vessel
#

i have so many open issues and pr's, i cant keep track of what im doing :(

vale ibex
#

I find github notifications are good

desert vessel
vale ibex
#

Since you can also see

dim pelican
#

Too good, I don't like the constant emails on things I commented on

desert vessel
desert vessel
#

but that would disable all mails

#

heh

fervent sage
dim pelican
#

I do, for all the ones Ive commented on. But it does it by default as far as I can tell

desert vessel
#

ah yes, the glory

vale ibex
#

I try to keep on top of my notifications lol

desert vessel
vale ibex
#

Click the bell icon in the top right

desert vessel
#

how did i not know that till now-

#

isnt this assigned

#

aaaaa

vale ibex
#

likely because you don't have a notification from that

#

Does it appear when you click assigned on the left?

desert vessel
#

nop

green oriole
vale ibex
#

ahh, I'd do it but then I wouldn't be counted as a reviewer lol

vale ibex
green oriole
desert vessel
#

fun fact: i found a bug in the linux kernel so i wanted to open a pr fixing it, but i went through the pr and the styling process and i quickly gave up

#

πŸ˜‚

vale ibex
#

since I pushed it it counts me as committing it I guess?

dim pelican
#

Β―_(ツ)_/Β―

vale ibex
#

Yea, I know that worked

green oriole
#

Huh

vale ibex
#

I'm disqualified from policy bot though

green oriole
#

He is being locked by policy bot and we need a devops to approve

dim pelican
#

I like that commit message

patent pivot
#

Approved πŸ‘½

vale ibex
green oriole
#

!otn a my brother from another mother ChrisJL

stable mountainBOT
#

:ok_hand: Added my-brother-from-another-mother-𝖒hris𝖩𝖫 to the names list.

vale ibex
#

lol

green oriole
#

Thank you sir Joe

patent pivot
#

anytime

green oriole
#

Oh actually it didn't devops approval but that works too lol

fervent sage
#

woo dpy2.0 in a couple minutes

#

big excite

vale ibex
#

time to rewrite all the games with buttons

green oriole
#

lol

fervent sage
#

theres no time for that, you must contribute to my discord lib instead mmLol

vale ibex
#

how come?

#

you should just be able to copy most of what exists in sirlance or bot

green oriole
#

Do we really have to, though? Threadevere most probably won't be around for too long anymore, and doesn't have any user facing command

vale ibex
#

True

#

I'm not against removing the help command entirely lol

desert vessel
#

or my vscode is screwed

vale ibex
#

we don't have custom help in @brisk belfry

desert vessel
desert vessel
#

wow, i thought vsc was screwed when i saw help.py being a part of /Library/Framwork/Python3.9

vale ibex
#

πŸ˜›

desert vessel
fallen patrol
#

its really not πŸ‘€

vale ibex
#

when that happens @brisk belfry's cogs will be moved there

fallen patrol
#

ah

desert vessel
#

most of the imports are referring to the files in the local bot repo so yeah

fallen patrol
#

I'm running a custom @dusky shore on v2 right now btw

#

haven't had any bugs so far

vale ibex
#

We just bumped lance to 2.0

desert vessel
#

but sir-threadevere isnt opening threads πŸ˜‚

vale ibex
fallen patrol
#

oh damn

#

I should uh, repull

fallen patrol
#

since you found all of the bugs that I haven't yet, probably

desert vessel
vale ibex
#

just check the PR πŸ˜„

desert vessel
#

it should make a thread in the nomination channel, correct ?

vale ibex
#

sir-lancebot#835

dusky shoreBOT
desert vessel
vale ibex
fallen patrol
#

you got all of the timestamp issues too, right?

vale ibex
#

not all messages, no

vale ibex
desert vessel
vale ibex
#

Don't think we relied on them being naive/aware

fallen patrol
#

welp you probably have a broken bug in there somewhere

vale ibex
#

in lance at least

vale ibex
patent pivot
vale ibex
patent pivot
#

is there anywhere you think we'd hit them?

#

yea

desert vessel
patent pivot
#

but I don't think as Chris says we rely on that

#

so I don't think where are a tonne of bugs

vale ibex
stable mountainBOT
#

bot/exts/halloween/hacktober-issue-finder.py lines 55 to 58

    if (ctx.message.created_at - self.cache_timer_beginner).seconds <= 60:
        log.debug("using cache")
        return self.cache_beginner
elif (ctx.message.created_at - self.cache_timer_normal).seconds <= 60:```
desert vessel
wise oriole
#

hi, I've read that d.py as a library is going to stop being developed? Would that affect this server's bots?

fervent sage
#

no

vale ibex
#

you should be able to type this and it'll work ```
@vale ibex for Helper!

Nomintated by:
react :+1: for approval, or :-1: for disapproval

stable mountainBOT
#

bot/exts/halloween/hacktober-issue-finder.py line 31

self.cache_timer_normal = datetime.datetime(1, 1, 1)```
fervent sage
#

at least, not in the near future

desert vessel
#

sir-lancebot#835

dusky shoreBOT
wise oriole
#

but the api is gonna be changing, and the api wrapper is not keeping up with it, thus losing its 100% coverage

brazen charm
#

it will affect the bots at some point, but not in a everything suddenly breaks way

desert vessel
#

^

#

bruv yeet it, im making another server and trying

#

:sadge:

#

anw, i'll do the pr of the sir-lancebot one and go to sleep

#

gn people :D

green oriole
#

Watch him ignore me

vale ibex
#

πŸ˜„

green oriole
#

Phew

fallen patrol
#

hm... if you're switching to dpy 2 you maybe want to update the paginator to use interactions πŸ˜›

green oriole
#

Ye ye

#

If only we had an issue for it πŸ₯Ί

vale ibex
#

paginator is the obvious one

#

but we'll do that separate from the actual upgrade on Python

green oriole
#

Yeah, let's minimize the breakage if it does break :D

#

We aren't time stressed, we can do it step by step

cold island
#

So are buttons just part of the message, and you can change which buttons appear by just editing it?

vale ibex
#

Yup

#

You can attach them as part of the message send api request, so no extra call

#

And then clicking a button triggers a callback

fallen patrol
#

yeah

fallen patrol
#

I just merged a pr to my bot to add an interaction paginator, if anyone wants inspiration πŸ˜›

vale ibex
fallen patrol
#

huh

fallen patrol
#
        if self.lock.locked():
            await interaction.response.send_message(
                ":x: Processing another user's button press, try again later.",
                ephemeral=True,
            )
``` could you explain?
#

I didn't use a lock on mine πŸ‘€

vale ibex
#

Anyone can click buttons, a lock means that it only processes one at a time

#

The use case for this view is to send an email to a user, so I want to avoid 2 people clicking a button triggering 2 emails

fallen patrol
#

ah, lock is probably not necessary for a paginator, then?

vale ibex
#

nope

fallen patrol
#

whew

#

thought I missed a horrible bug

vale ibex
#

πŸ˜›

fallen patrol
#

since i just merged the pr after a successful review

#

like

#

hell yeah it works great

green oriole
#

... There is only one page?

fallen patrol
#

yeah?

#

There's not a boatload of extensions yet smh

green oriole
#

I guess it does work then

fallen patrol
#

smh

#

and the help command is still default so can't show it there πŸ˜”

vale ibex
#

How did you test it worked with the buttons ?

fallen patrol
#

wrote a custom cog as a plugin

#

with multiple options πŸ™‚

cold island
#

So bulky though

fallen patrol
#

its better when you aren't paginating 5 characters

green oriole
#

Yeah, I guess it is more bulky than reactions

fallen patrol
#

well yeah

cold island
#

I guess that's more mobile friendly

fallen patrol
#

the buttons are actually smaller on mobile

#

On android:

celest charm
fallen patrol
#

Lmao

fallen patrol
brisk brook
#

@cold island seems like your apology wasn't accepted

cold island
#

lol

#

I accidently hit approval instead of request changes

fallen patrol
#

I commented on your review

cold island
fallen patrol
#

Oh, true

cold island
#

Thanks @brisk brook, your explanation happened to solve something I was just doing πŸ˜„ πŸ‘Œ

brisk brook
#

Ah lemon_infant

fallen patrol
#

the last person to get it became a helper a week later lol

severe tangle
#

Hey @vocal wolf, so what should be done about bot#1786?

#

bot#1786

dusky shoreBOT
vocal wolf
#

wait until it gets reviewed, I guess

severe tangle
#

By Stelercus?

vocal wolf
trim cradle
#

@vocal wolf I'm not authorized to merge the pull request and I'm really upset about it.

vocal wolf
#

lmfao

vocal wolf
trim cradle
vocal wolf
#

tis a shame

trim cradle
severe tangle
#

lmao

dim pelican
#

@timid sentinel this is what I get for trying to push commits away from my clone set up

fallen patrol
vocal wolf
fallen patrol
#

(windows and linux github actions all run on azure)

fallen patrol
#

it now doesn't show all of the info it used to

#

@vale ibex you initially publicized this ^

vale ibex
#

What do you think it missing?

fallen patrol
#

look at my screenshot

vale ibex
#

That screenshot looks like it looks now, just with code blocks removed

fallen patrol
#

like this:
<picture should be here>

#

well, they aren't showing up for me πŸ€”

#

even changed browsers

fallen patrol
#

-i

#

that's where I am?!

#

yet clicking that link worked??

dim pelican
#

I see two photos in the guide?

fallen patrol
#

whatevs

austere hornet
#

@vale ibex So I did some thinking about the Mastermind issue and decided that I would try something else. So I thought of a different idea and created a new issue for it. If you are available, could you please take a look at sir-lancebot#836? Would much appreciate it. Tysm!

dusky shoreBOT
austere hornet
#

Btw I hope that this is not already on Sir Lancebot - 😬

vocal wolf
#

@vale ibex gitkraken is so good for merge conflicts holy crap

#

I've never had such an easy time fixing them up for a squash

dim pelican
#

I know what I'm going to checkout next

static canyon
#

I'm working on bot#1787 and have come across an issue. My API request is returning a non-expanded version of users, where I need the expanded version (examples below). How can I make it return expanded users?

Infraction search (expanded):py [{'id': 1, 'inserted_at': '2021-08-31T20:37:12.255534Z', 'expires_at': None, 'active': False, 'user': {'id': 442244135840382978, 'name': 'TizzySaurus', 'discriminator': 9615, 'roles': [476190141161930753, 476190408871772171, 476190357927886848, 637792718852063282], 'in_guild': True}, 'actor': {'id': 442244135840382978, 'name': 'TizzySaurus', 'discriminator': 9615, 'roles': [476190141161930753, 476190408871772171, 476190357927886848, 637792718852063282], 'in_guild': True}, 'type': 'note', 'reason': 'testing', 'hidden': True}]Infraction (non-expanded):py [{'id': 1, 'inserted_at': '2021-08-31T20:37:12.255534Z', 'expires_at': None, 'active': False, 'user': 442244135840382978, 'actor': 442244135840382978, 'type': 'note', 'reason': 'testing', 'hidden': True}]

dusky shoreBOT
cold island
#

@static canyon Found it

static canyon
#

So /infractions/{infr_id} would be /infractions/expanded/{infr_id}?

#

@cold island

cold island
#

You can try

static canyon
#

yep, true

cold island
#

Btw @brisk brook did you happen to test it before approving?

brisk brook
#

Which one?

cold island
#

Infraction by ID

brisk brook
#

No, if you need me to I'll do that tomorrow

static canyon
#
bot_1       | 2021-08-31 21:12:14 | bot.exts.backend.error_handler | DEBUG | API responded with 404 for command infraction```I'll try `/infractions/{infr_id}/expanded` and see what happens
cold island
brisk brook
#

Ah, right

fallen patrol
static canyon
#

Yeah

#

And it does work at the end

austere hornet
#

Question: If I want to assign myself an issue, but I won't have time to implement the new feature for a long time, is it ok to still assign myself the issue or should I wait until I have time and then assign myself?

cold island
#

Preferably the latter

austere hornet
cold island
#

Think about it this way: issues are opened for things we want to see implemented. If you're assigning yourself without intending to work on it in the near future then you're effectively stalling the feature/bug fix

dim pelican
#

Is sir-lancebot#834 in the right place? Or should this be in meta or .github?

austere hornet
desert vessel
vocal prairie
#

@desert vessel You don't need to close the issue, what I said just means that it should probably wait a little bit. It's a great idea.

desert vessel
#

as xithrus mentioned, wasnt this solved in another pr ?

#

@vocal prairie here ^

vocal prairie
#

No, that just adds the tag group functionality, which could be used to implement this feature.

desert vessel
#

oh well,

#

i've reopened it for now

#

thanks for the clarification!

short snow
#

i just noticed seb doesn't have the project leads role, isn't he the project lead for API?

#

sir-lancebot#618 Rebasing upstream main into my branch, I have messed up the whole commit history πŸ˜” What should I do now? Let it stay? And rebased it to get the new cs/python quiz questions and the rapidfuzz change

dusky shoreBOT
fallen patrol
#

first off, don't do anything else locally. There are ways to fix it from where you are

#

The previous commits still exist in your local copy. but any commands you run to fix it can fuck it up and get them gone

#

@short snow^

short snow
#

umm probably, also what's the correct way to rebase to not let such things happen in future

fallen patrol
#

which is an interactive rebase

dim pelican
#

Also git reflog to check where the HEAD was at when the changes were made, then git reset (or revert?) HEAD~<number to reset to>

vocal wolf
old shard
#

.bm doesn't seem to be working

cold island
#

.bm 882575523195281408

vale ibex
#

Ah, permissions_in has been removed in d.py 2.0

#

Go forth and review sir-lancebot#838

dusky shoreBOT
dim pelican
short snow
#

Due to my half-yearly exams, I will be going on a 1 and a half month break after the end of this week. So if anyone has got some time, it would great to get my PRs complete πŸ™‚ Thanks!

dusky shoreBOT
static canyon
#
    async def cog_command_error(self, ctx: Context, error: commands.CommandError) -> None:
        """Handles errors for commands within this cog."""
        ...

        elif isinstance(error, commands.ConversionError):
            await ctx.send(f":x: A corresponding infraction for {XXXX} could not be found.")

        ...```does anyone know how I can get the value that failed the conversion from the error?
vale ibex
#

You should be able to str() the error to get what was passed into the error when it was raised

#

what actually gets passed into the raise error depends on the converter

static canyon
#

Something like Status: 404 Response: {'detail': 'Not found.'}

vale ibex
#

look like it's raising a BadArguement inside the converter

#

you could create the error, add a custom attr and then raise it I guess

static canyon
vale ibex
#

This is what I'm seeing atm ```py
class Infraction(Converter):
"""
Attempts to convert a given infraction ID into an infraction.

Alternatively, `l`, `last`, or `recent` can be passed in order to
obtain the most recent infraction by the actor.
"""

async def convert(self, ctx: Context, arg: str) -> t.Optional[dict]:
    """Attempts to convert `arg` into an infraction `dict`."""
    if arg in ("l", "last", "recent"):
        params = {
            "actor__id": ctx.author.id,
            "ordering": "-inserted_at"
        }

        infractions = await ctx.bot.api_client.get("bot/infractions", params=params)

        if not infractions:
            raise BadArgument(
                "Couldn't find most recent infraction; you have never given an infraction."
            )
        else:
            return infractions[0]

    else:
        return await ctx.bot.api_client.get(f"bot/infractions/{arg}")
#

is this the converter you're talking about?

#

So I'm guessing the 404 is from that final line

#

so you could add some logic there to raise if it returns a 404 there

#

api_client.get handles a 404 locally and raises a ResponseCodeError

static canyon
#

I'll show what I'm doing in a second

#
class Infraction(...):
    async def convert(...):
        ...
        else:
            try:
                return await ctx.bot.api_client.get(f"bot/infractions/{arg}/expanded")
            except ResponseCodeError as e:
                if e.status == 404:
                    raise BadArgument(f"Failed to convert '{arg}' to an infraction.", arg)
                raise e
``````py
async def cog_command_error(...):
    ....
    elif isinstance(error, commands.BadArgument):
        await ctx.send(f":x: Could not find a corresponding infraction for `{error.args[1]}`.")
    ...``` @vale ibex this is what I'm doing now
#

So i catch the error in the converter and raise a different error with arg, which is caught in cog_command_error

#

Which results in this behaviour

vale ibex
#

What's the desired behaviour?

static canyon
#

I'm happy with what I've got now

vale ibex
#

Ah cool

static canyon
#

Just not sure if there's a tidier way that you/anyone can think of

vale ibex
#

to avoid error.args[1] I wonder if you can do ```py
bad_arg_error = BadArgument(f"Failed to convert '{arg}' to an infraction.")
bad_arg_error.infr_search = arg
raise bad_arg_error
...
f":x: Could not find a corresponding infraction for {error.infr_search}."

static canyon
#

Hmm

vale ibex
#

not saying one is better than the other

static canyon
#

It gives a linting error in the handler saying BadArgument has no attribute 'infr_search'

vale ibex
#

hmmm, guess you can't do that then

static canyon
#

Although I think the actual test lint passes (poetry run task lint)

static canyon
#

So I've got the option

#

Although I do personally prefer just using .args[1]

#

Yeah, I'll keep it .args[1]

vale ibex
#

πŸ‘Œ

desert vessel
#

can someone help me with this

#

i have pylance installed

#

but there are no suggestions

brazen charm
#

you could create a subclass of the exception to properly set the attribute

desert vessel
#

alright πŸ‘

#

for me, vscode autosuggestion generally dont work

static canyon
# vale ibex πŸ‘Œ

I've tweaked things around a bit but am happy now so going to commit and push πŸ‘

vale ibex
#

Wait, doesn't the api wrapper deal with errors in maybe_raise_for_status already

#

So it should be raising ResponseCodeError for 404 already afaik

brazen charm
#

It'll go to the generic "According to the API, your request is malformed." message for api errors

#

(or whatever is appropriate for the error code)
Not sure how helpful that is

vale ibex
#

True

#

The reason I mention it is since ResponseCodeError already has custom attrs

#

so you could add another one for the endpoint url and then get it from there

static canyon
#

I've already done this now lemon_sweat

#

My head hurts too much to change it now lol

#

Just gonna push this because it works and I've spent way too many hours on this already

vale ibex
#

Sure

static canyon
#

bot#1804 is finally up

dusky shoreBOT
static canyon
#

I did not expect that to take almost 2 hours lol

dim pelican
#

Is it more common to ping people in GitHub or here to request a re-review?

short snow
#

re-request on github

#

trivia_night πŸ‘€ esoteric_challenges πŸ‘€

#

are these hints to the next events @thorny obsidian

thorny obsidian
#

haha yup. Those are future events. Still in the planning stages though. I'm trying to scope out the functionality for it and that lead me to... well this issue

short snow
#

weeeee

#

wait when did u loose our your admin role

thorny obsidian
short snow
#

Oh πŸ˜”

dim pelican
#

@fervent sage sir-lancebot#833 is ready for you, there have been some changes since my last requested review and now

dusky shoreBOT
fallen patrol
#

Ignore me, sorry

#

!guild

stable mountainBOT
#
Server Information

Created: <t:1483877013:R>
Voice region: europe
Features: DISCOVERABLE, ANIMATED_ICON, BANNER, ROLE_ICONS, INVITE_SPLASH, PREVIEW_ENABLED, NEWS, VIP_REGIONS, PARTNERED, VANITY_URL, PRIVATE_THREADS, MEMBER_VERIFICATION_GATE_ENABLED, RELAY_ENABLED, COMMUNITY, NEW_THREAD_PERMISSIONS, THREADS_ENABLED, SEVEN_DAY_THREAD_ARCHIVE, THREE_DAY_THREAD_ARCHIVE, WELCOME_SCREEN_ENABLED
Roles: 90
Member status: status_online 53,398 status_offline 194,213

Members: 247,611

Helpers: 124
Moderation Team: 31
Admins: 14
Owners: 3
Contributors: 40
Leads: 14

Channels: 241

Category: 32
News: 9
Staff: 65
Stage_Voice: 1
Text: 125
Voice: 9

fallen patrol
#

Role icons O.o

#

Don't see events on there tho

patent pivot
#

no β€” it should use allowed mentions

#

eh

#

i mean

#

why

#

it can't ping anyone

trail pilot
#

Question - would a .project command in sir lancebot be useful?

I know we already have !projects, but this is an assumption that most people won't really take their time to check out the webpage and actually find a project they link

The .project command I'm thinkinig of would just get a single idea from a YAML with different categories, like 'beginner', 'intermediate', and 'advanced'

cold island
#

It can't

#

It uses allowed mentions and I just checked now with a few roles to make sure

trail pilot
#

I'm wondering what your all's thoughts are because I think it's probably better to talk here before making an issue for something that may not even be needed

cold island
#

We turned down previous suggestion to maintain our own list of projects

#

So I don't think this is much different

#

There are great resources out there, Ned's list is one of them

trail pilot
#

Ah, alright

#

Thanks for letting me know

cold island
#

If you had a way to pull from an existing list (without burdening the host), that would be something to consider

trail pilot
cold island
#

If whatever that is has a public API then better. And with caching so it's only fetched once while the bot is up.

I don't know how good this is, but if it's interesting to you you can open an issue so that it can be discussed more thoroughly, preferably with the website you'd like to fetch the projects from.

trim cradle
#
Traceback (most recent call last):
  File "/home/steele/.cache/pypoetry/virtualenvs/site-AnmfhEHv-py3.9/lib/python3.9/site-packages/environ/environ.py", line 273, in get_value
    value = self.ENVIRON[var]
  File "/usr/lib/python3.9/os.py", line 679, in __getitem__
    raise KeyError(key) from None
KeyError: 'SECRET_KEY'

Tell me the secret

patent pivot
#

just need to set a dummy secret key env var

trim cradle
patent pivot
#

true i lied πŸ˜”

#

we'll just hardcode the secret key and operate pythondiscord.com on a trust basis from hereon

trim cradle
#

Anyway a bunch of tests failed. I suggest that you force approve my PR and then we'll fix it in prod.

vale ibex
#

@reef tinsel

#

This would be the best place to ask questions/get feedback on designs πŸ˜„

reef tinsel
#

πŸ‘

vale ibex
#

ref bot#1765 for others out of the loop

dusky shoreBOT
reef tinsel
#

Does the bot have any slash commands? (In other words, do I need to add application.commands for inviting to my server?

vale ibex
#

Nope, no slash commands in any of our main bots atm

#

since d.py doesn't support them

reef tinsel
#

Ok!

#

Having had a look at the patreon page, I presume the 3 tiers you mention correspond to the ones that gives the cosmetic role colour?

vale ibex
#

yup yup

#

The role IDs are in the issue

reef tinsel
#

Yep

#

What ext should I put the code in?

vale ibex
#

I'd make a new one

reef tinsel
#

Ok

vale ibex
#

I think putting it in the info folder makes the most sense

reef tinsel
#

I guess I should add some config in config.yml-guilds-roles so that it is easy to set this up for testing in development guilds?

vale ibex
#

Yea, add the role ids in the issue into the config-default.yml

#

then it can be overridden to your test server IDs in the config.yml file

reef tinsel
#

πŸ‘

reef tinsel
#

When I run docker-compose up, I get some sort of file not found error:

What might be the problem?

#
Traceback (most recent call last):
  File "docker\api\client.py", line 214, in _retrieve_server_version
  File "docker\api\daemon.py", line 181, in version
  File "docker\utils\decorators.py", line 46, in inner
  File "docker\api\client.py", line 237, in _get
  File "requests\sessions.py", line 543, in get
  File "requests\sessions.py", line 530, in request
  File "requests\sessions.py", line 643, in send
  File "requests\adapters.py", line 439, in send
  File "urllib3\connectionpool.py", line 670, in urlopen
  File "urllib3\connectionpool.py", line 392, in _make_request
  File "http\client.py", line 1255, in request
  File "http\client.py", line 1301, in _send_request
  File "http\client.py", line 1250, in endheaders
  File "http\client.py", line 1010, in _send_output
  File "http\client.py", line 950, in send
  File "docker\transport\npipeconn.py", line 32, in connect
  File "docker\transport\npipesocket.py", line 23, in wrapped
  File "docker\transport\npipesocket.py", line 72, in connect
  File "docker\transport\npipesocket.py", line 52, in connect
pywintypes.error: (2, 'CreateFile', 'The system cannot find the file specified.')

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "docker-compose", line 3, in <module>
  File "compose\cli\main.py", line 81, in main
  File "compose\cli\main.py", line 200, in perform_command
  File "compose\cli\command.py", line 60, in project_from_options
  File "compose\cli\command.py", line 152, in get_project
  File "compose\cli\docker_client.py", line 41, in get_client
  File "compose\cli\docker_client.py", line 170, in docker_client
  File "docker\api\client.py", line 197, in __init__
  File "docker\api\client.py", line 221, in _retrieve_server_version
docker.errors.DockerException: Error while fetching server API version: (2, 'CreateFile', 'The system cannot find the file specified.')
[15916] Failed to execute script docker-compose
short snow
#

docker environment is running

reef tinsel
#

I don't have any experience with docker, so I don't know what you mean?

short snow
#

"docker" isn't running on your system and docker-compsoe is trying to interact with it basically

#

are you using docker desktop?

reef tinsel
vale ibex
#

make sure docker desktop is running

reef tinsel
#

so i presume yes to our question

reef tinsel
short snow
#

you would see a docker icon on the taskbar on the right hand corner, like ‡️

vale ibex
#

yea, it doesn't need to be fully open, as long as it's started and the icon in on your taskbar, it's fine

reef tinsel
#

docker is telling me something about WSL, do I need WSL, or can I use docker without this?

vale ibex
#

Yea, you'll need to have wsl unless you have windows pro and can use hyper-v

#

hyper-v is slower anyway, so you likely want wsl

reef tinsel
#

ok

vale ibex
#

the link you posted above has info on how to setup the wsl2 backend

green oriole
#

Hyper-V is also windows pro edition only

short snow
#

offtopic, but why does it need wsl2 backend or hyper-v?

green oriole
#

But you already said that

#

πŸƒ

thorny obsidian
#

Because it needs to run on a linux system at some point.

gritty wind
#

Different arch for windows

green oriole
#

It needs a running Linux kernel, yeah

gritty wind
green oriole
#

Containers are just an abstraction made around various kernel isolation systems

#

All of your containers are in fact running on the host kernel, just in different groups

vale ibex
#

docker desktop is just a glorified VM manager kek

green oriole
#

That's the main difference with regular VMs

reef tinsel
#

once i've installed WSL, will I also need to install a version of linux, or will docker manage that for me?

green oriole
#

You need to install any version of WSL2 and you are good to go

gritty wind
#

Just WSL, though you may find other things helpful for other projects

#

for docker, just wsl though

reef tinsel
#

ok, so no need to install a linux

gritty wind
#

no

green oriole
#

WSL is Linux

vale ibex
#

"Windows subsystem for linux"

reef tinsel
#

im confused

gritty wind
#

I know, which is why I said no 🀑

reef tinsel
#

because i on the wsl page it says something about installing a linux

gritty wind
#

WSL is linux like Akarys mentioned, you just need to install it

green oriole
#

Installing WSL goes in two step, enabling it then installing a specific distro from the store

gritty wind
#

That's an update fie you have to install

vale ibex
#

WSL is a backend for linux, if you want access to a linux environment, then you need to install a linux distro too. But since you just want it for docker, no need to install a distro

gritty wind
#

Source: I just set up a new PC

green oriole
#

Oh that's interesting

#

I guess it boots up it's own WSL distro, which makes more sense when you think about it

vale ibex
#

yup yup

gritty wind
#

It does yeah

C:\Users\hassa>wsl -l --all
Windows Subsystem for Linux Distributions:
docker-desktop (Default)
Ubuntu
docker-desktop-data

#

Ubuntu is ubuntu 20 from the store

dim pelican
#

When you are all testing changes, do you have to stop and reload docker? That is what I've been doing but didn't know if there was a different way

vale ibex
#

yea, or reload the cog

short snow
#

while working on the bot?

#

yeah I reloed the cog

#

!c r <cog> or .c r <cog>

vale ibex
#

to make it faster you can do docker-compose up bot

#

or sir-lancebot if you're working on that

#

that will mean when you ctrl+c to stop it, the other things still run in the background

#

so starting it back up is faster

dim pelican
#

Sweet, I figured I was doing something clunky

#

Is .bm whitelisted yet? it is not

vale ibex
#

in help channels yes, not in all channels yet

#

there's an issue for it

short snow
#

@vocal wolf Uhhh just to inform you as you are the triage lead, from tomorrow I am going on a break due to my half-yearly exams for a month (or a bit more). So in case some of PR gets on high priority, any1 can takeover it if I don't respond.

reef tinsel
vale ibex
#

the former

reef tinsel
#

Ok

brazen charm
dim pelican
brazen charm
#

Docker is there to make the setup easier and reproducible, but for the bots itself it's already fairly easy through poetry

#

I have redis, postgres and the site running in docker and the bot has a run config in pycharm and is ran directly

dim pelican
#

Hmmmmm, I use VS Code but you are saying I just run the __main__.py to make the bot live

short snow
#

poetry run task start ^^ would do that

#

yeah

gritty wind
short snow
#

but inside the venv

#

I only use docker for redis when needed, otherwise run everything normally

dim pelican
#

Wow I'm over here like "Well I made a small change, let's stop Docker, then run it again to test"

gritty wind
#

The default docker compose does load all changes from your harddrive

short snow
#

and for changes to cogs, i would reload the cog

gritty wind
#

Though I personally do what Numerlor does

brazen charm
#

reloading the extension usually works and is faster than a normal restart too, but there are things that will persist

dim pelican
#

To summarize for my future self:

Options:

  1. Run Docker, make change, Stop Docker, Run Again (bad) docker start -d snekbox redis postgres web
  2. poetry run task start from the root, if outside the venv
    task start if inside the venv
  3. py -m <bot/sir-lancebot> from the root, inside the venv
  4. Set up a run task (automatically run py -m <bot/sir-lancebot>)
  5. If changing a cog, reload cog from the discord channel with !c r <cog> or .c r <cog>
brazen charm
#

You shouldn't run the main file directly as it won't initialize the package

#

pycharm can be set up to run a module, not sure about vscode

dim pelican
#

What is the setup for that run task?

brazen charm
#

probably python -m bot

dim pelican
#

So like my new edit above?

gritty wind
#

The python extension for VSCode makes it possible, but I haven't played around with it much

#

What you have looks right

dim pelican
#

Sweet, going to bookmark that for myself

gritty wind
#

Maybe add a note about starting the other tasks too

#

docker start -d snekbox redis postgres web should do ya

#

or an override

short snow
#

so you can run task start if you are inside teh venv

#

no, remove the poetry, just task start (context: they edited the msg)

#

yep

dim pelican
#

Time to open an issue on site to add this to the contributing guidelines lol. Surely other people have gone through what I'm running into

dim pelican
#

site#574

clever wraith
#

Congrats @solar locust on contributor

dim pelican
#

You are now DD

clever wraith
#

Yes

#

Shoot

reef tinsel
#

is this the correct syntax for getting a role id? (saved in config.yml)

from bot.constants import Guild

patreon_tier_1_role = Guild.roles.patreon_tier_1
vale ibex
#

it would be ```py
from bot.constants import Roles
patreon_tier_1_role = Roles.patreon_tier_1

reef tinsel
#

ok

#

looking at constants.py, do I need to add my 3 roles to the Roles class?

#

I presume so

vale ibex
#

yup yup

#

and makes the constants available to the project

short snow
#

2 new contribs πŸŽ‰

vale ibex
#

maybe more, news to come soon

#

just waiting on some responses, have an announcement prepped

short snow
#

Ooo

warm vigil
#

Hey, is any of PyDis bots slated to move over to slash commands via HTTP?

vale ibex
#

no plans for it atm, no

#

our short term plans are to migrate to d.py 2.0 for the other interactions

#

and then wait a few weeks/months to see what other libs arise

#

ideally we don't want to re-write our bots

warm vigil
#

ok, it's fine, we are having VC discussion about if your bot is call and response, get ahead of curve, switch to slash commands over HTTP

brazen charm
#

@vale ibex site#571 will also need to be merged before the optional base url can be used w/o an error

vale ibex
#

out of interest was there a need to swap the field order?

brazen charm
vale ibex
#

yea makes sense

vocal wolf
#

@brisk brook about the __init__ return annotations, there was about 20 that had been annotated and about 50 that had not been.

#

So I just went ahead and removed the 20 annotations

brisk brook
#

Yeah, any plans for how we should keep this consistent?

vocal wolf
#

no plans as of now

brisk brook
#

Alright

vocal wolf
#

hopefully people just notice other files, not sure how we should enforce it

#

enforce it automatically, that is

brisk brook
#

Yeah, we'll just have to remember it.

vale ibex
#

add mypy

vocal wolf
#

lol

brisk brook
#

I don't think there's a way to setup some CI or tool to check only that

vocal wolf
#

I feel like there might be...?

#

ooh

#

regex in a workflow?

brisk brook
brisk brook
#

There has to be a simple regex CI

vale ibex
#

ehhh, I'm not too fond of having something in the style guide that we can't enforce via pre-commit or linting

#

since it's sort of hidden to contribs until PR

vocal wolf
#

If def __init__\(.*\) -> None finds anything, workflow fails, but an entire workflow just for that is probably not worth it

vale ibex
#

I'd be up for adding mypy tbh, if it wasn't for the huge change we'd need to make to the current repo

#

even then I'm still on the side of adding it

vocal wolf
#

and we're also removing seasons soon

brazen charm
#

the ignore could just be removed from the plugin settings to the annotation is used everywhere

brisk brook
brazen charm
dusky shoreBOT
brisk brook
#

Yup

vocal wolf
#

I haven't seen many annotations for constants

#

We don't use typing.Final within Sir lance at all

brisk brook
#

I am pretty neutral on it, we don't really use strict type checking so the benefit of a failing workflow from breaking those rules are gone. But I thought I'd bring it up

#

Also yes I mean for all constants, it was just those lines that reminded me of it

vocal wolf
#

according to my regex search in pycharm, we've never annotated constants

#

unless I've messed up my regex search

brisk brook
#

No I don't think we have

#

So yeah we might as well keep it like that, not a big deal

vocal wolf
#

ye

dim pelican
#

Wait are Numerlor and Bluenix not contributors?

#

Sorry for the ot tangent

vocal wolf
brisk brook
#

So previously we did import typing as t. So code that used Iterable would be as follows: def test(arg: t.Iterable[str]), is this not the case?

#

The diff was being weird on my phone, but I see now that you have indeed changed it

#

I'll make it as resolved

vocal wolf
#

uhhhhh

#

let me check what it previously was

vocal wolf
brisk brook
#

This diff didn't show up for me

#

So I thought you only changed the import, and not the code that used it

vocal wolf
#

according to git blame it was like this ever since I created the file

vocal wolf
brisk brook
#

Even better, Optional[re.Match] should work

vocal wolf
vocal wolf
#

ah, that's why

#

was searching in re docs lol

brisk brook
#

πŸ˜…

vocal wolf
#

This PR is getting ridiculous, there's just so many things that I'm not catching

#

@clever wraith oh no

#

wait why are we wanting to annotate tuples but haven't covered dicts?

#

uhhhhhh

#

I am thinking about this one lol

gritty wind
#

A lot of those can’t be easily modeled just with dicts

#

I’d be for something like pydantic if it helps

vocal wolf
#

Probably should be annotated only if it's not almost immediately realized by the program itself.

gritty wind
#

But I personally prefer dicts that are annotated, because otherwise they are kinda a PITA to work with

dim pelican
#

Pain In The Ass

gritty wind
#

Yup

#

What is this referring to

dim pelican
#

or delicious pocket bread, depends on the context

gritty wind
#

I mean, good annotations can be quite delicious

dim pelican
#

This part?

only if it's not almost immediately realized by the program itself.

molten perch
#

There are cases when dict is already nested like tuple[int, dict] in that case it might be helpful to annotate, but to be totally honest, it'd look a bit "scary" to newcommers.

brazen charm
#

TypedDicts would be nice for all the api interactions

molten perch
celest charm
#

or dataclasses and dataclass-factory, it's much smaller than pydantic AFAICT

static canyon
#

@celest charm ```py
import random
from typing import Optional

def return_int_or_none() -> Optional[int]:
if random.randint(0, 1) == 0:
return 1
return None

def expects_int(arg: int) -> None:
return

var = return_int_or_none()
assert isinstance(var, int)
expects_int(var)```this works perfectly with the assert, but doesn't work without it

#

Question is, do we want to assert or just an if?

gritty wind
#

What if you add an explicit type hint to the variable

celest charm
#

it will fail

#

because that's a lie

#

there are also casts

static canyon
static canyon
#

Hmm

#

Sopy var: t.cast[int, var] = always_returns_int_although_linter_doesnt_realise()?

celest charm
#

no, it's a function

#

cast(int, var)

#

it doesn't actually do anything -- it just signals to the typechecker

static canyon
#

@celest charm ```py
import random
from typing import Optional, cast

def return_int_or_none(val: str) -> Optional[int]:
if val == "return":
return 1

def expects_int(arg: int) -> None:
return

var = return_int_or_none("return") # val=="return" so will always return 1 but linter doesn't realise
cast(int, var)
expects_int(var)```so like this?

#

Sorry had the ping copied in that

celest charm
#
var = return_int_or_none("return")  # val=="return" so will always return `1`
expects_int(cast(int, var))
#

no worries, I like pings πŸ™‚

static canyon
#

right, yeah

#

So in cases where we know it's going to be correct, use cast, and when we're not sure, use assert?

celest charm
#

cast is useful for stuff like list[tuple[T, int]] which you can't really assert on

static canyon
#

Right yeah

celest charm
#

assert should also be used only in places where the condition should only be false in the presence of a bug

static canyon
#

So potentially even```py
def expects_int(arg: cast(int, arg)) -> None:
return

expects_int(var)```?

celest charm
#

???

static canyon
celest charm
#

You can't put cast into annotations

static canyon
#

That doesn't seem right actually

#

Yeah

celest charm
#

No I mean, you can't do isinstance(x, list[tuple[T, int]]), But if you believe that it is the case, do cast(list[tuple[T, int]], x)

static canyon
#

Right

static canyon
celest charm
#

If we're not sure, throw a normal exception

static canyon
#

Well that's what assert would do if it was wrong

celest charm
#

assert is not for error-checking, it's for establishing some invariant. In production you can disable asserts

#

well, it depends on where it is used

static canyon
#
def return_int_or_none() -> Optional[int]:
    if random.randint(0, 1) == 0:
        return 1

var = return_int_or_none()  # we don't know if `var` is an `int` or `None`, so we `assert` to check
assert isinstance(var, int)
expects_int(var)

##

def return_int_or_none(val: str) -> Optional[int]:
    if val == "return":
        return 1

var = return_int_or_none("return")  # we know it's an `int` so use `cast`
expects_int(cast(int, var))```
#

Or would the top be likepy if not isinstance(var, int): raise TypeError("we need an int") expects_int(var)?

static canyon
#

Right

celest charm
#

well... the example is a bit contrived

#

asserts are usually internal checks, while the errors on the boundary of your function/class should be proper exceptions

dim pelican
#

Is it bad that I've only used assert in unit testing?

static canyon
#

I'm trying to think of something

celest charm
#
def inv_sqrt(x: float) -> float:
    if x <= 0:
        raise ValueError("nope, that's not going to fly here")

    sqrt = x ** 0.5
    assert not isinstance(sqrt, complex)
    assert sqrt > 0
    return 1 / sqrt
#

also a bit contrived, but eh

static canyon
#
def expects_tuple_of_list_of_int_or_str(arg: tuple[list[Union[int, str]]]) -> None:
    return

var = returns_expected_or_none()  # how do I validate `var` is the expected here?
expects_tuple_of_list_of_int_or_str(var)```
celest charm
#

jesus take the wheel

static canyon
#

There are situations like that in the bot code

#

So we'd have to deal with that

celest charm
#

why would you need to validate the return type of that thing?

static canyon
#

I suppose it's or_none so we can just do if var is not None

celest charm
#

in that case yes, check if it's none

#

sometimes the invariant is really way too complex, so you'll have to ignore some errors

static canyon
#

And when you say "ignore some errors" do you mean add a literal # ignore: [err_type] to the end of the line?

#

Or do you mean "as a human, ignore it"

static canyon
#

right, okay

#

Jesus this is gonna take a while lol

celest charm
#

I still wonder if we should put effort into all this

#

are we planning to do it for sir-lancebot as well?

static canyon
#
for role_id in role_ids:
    if (role := guild.get_role(role_id)) is not None:
        members += len(role.members)
    else:
        raise NonExistentRoleError(role_id)

return {name or role.name.title(): members}  # `role` is `Role` but linter thinks it's `Optional[Role]`
```so how would I cast here?
celest charm
#

cast(Role. role).name.title()
or
assert role is not None before that

#

actually, the correct error would be: "role is possibly unbound"

#

which is what pyright gives

static canyon
#
  • cast(Role, role) (type goes first)
celest charm
#

oh, right

#

fixed

static canyon
#

The error mypy gives is error: Item "None" of "Optional[Role]" has no attribute "name"

#

So I'd do {name or cast(Role, role).name.title(): members}

celest charm
#

yeah

molten perch
celest charm
#

testing? pithink

vocal wolf
#

We don't want to enforce dictionary type hinting since it'll get bloated very fast (large type annotations), so most dictionaries will remain the same.

molten perch
#

I was thinking about pattern matching(might've used the wrong word), realised it's a nonsense for now. I played to much with it πŸ˜„

celest charm
#

It will add more code, but it does buy us explicitness (and checking that the API actually returns what we expect)

#

we could remove all type annotations, and the code would become more compact

static canyon
#
error: Value of type "Infraction" is not indexable```
```py
async def ...(..., infraction: Infraction, ...) -> ...:
     ...
     infraction['id']
     infraction['active']```
Can I fix these with a `cast` as well? mypy thinks `infraction` is of type `Infraction` but it's actually a `dict`
#

I think there was an issue which might fix that actually

celest charm
static canyon
celest charm
#

ahh

#

yeah we did fix it somehow, right?

#

so that's to be fixed in a different place

molten perch
celest charm
#

I wasn't implying we should do that

static canyon
#

It was numerlor that fixed iirc

celest charm
static canyon
#

bot#106

dusky shoreBOT
static canyon
#

Ah right

#

I need to do likepy Infraction = dictat the bottom of constants.py I think

#

Which in fact is already there

#

Hmm

#

It doesn't seem to be working

molten perch
celest charm
#

I will never vouch for removing all type annotations, no...

vocal wolf
#

ok

#

I hope I'm done with this annotation PR

static canyon
vocal wolf
#

pretty sure bot is fine

#

at least mostly

static canyon
#

It's not lol

vocal wolf
#

well

#

damn

static canyon
#

xD

#

Do you have any ideas about Infraction not being indexable? @celest charm

celest charm
#

I have no idea

static canyon
#

Welp fk

#

Numerlor's PR should've fixed it but it isn't working

celest charm
# celest charm I meant that type annotations add more code, but they buy explicitness and stati...

Example from xkcd: latest_comic_info is hinted as Dict[str, Union[str, int]]
It's not clear at all what's in this dict, you just have to go to the API docs. It goes completely contrary to our style guide that mandates docstrings and type annotations even on functions/classes which are totally clear without them (like setup() or cog classes)

Instead, you can just do something like ```py
@dataclass
class Xkcd:
num: int
day: str
month: str
year: str
safe_title: str
img: str

and use a library or a hand-written utility function to create these from a json dict
#

.source xkcd

dusky shoreBOT
#
Command: xkcd

Getting an xkcd comic's information along with the image.

Source Code
celest charm
#

I'm not saying we should refactor the code to do that, just some rationale as to why it might make sense to write in this way

molten perch
#

Anyways, you wouldn’t remove the typehints totally, just use a different approach. (like in fastapi)

celest charm
#

what are you talking about?

molten perch
#

You said using a library like that would mean removing typehints totally

celest charm
#

Hm?

static canyon
#

@brazen charm you PRd bot#1731 which I think is what I need here but it doesn't seem to be working

My issue is that in a command I'm typehinting an argument to the Infraction class, and then subscripting it (infraction['active'], infraction['id'] etc.).

Unfortunately mypy is raising an error for this, stating that Value of type "Infraction" is not indexable, despite us having Infraction = t.Optional[dict] in constants.py, which should cover for this. Fwiw I changed to Infraction = dict in case it was the Optional breaking it but still same issue.

The docs do state that mypy supports TYPE_CHECKING so that shouldn't be the issue either.

Please ping on response

dusky shoreBOT
celest charm
#

I think there's some misunderstanding between us

molten perch
#

No, not at all. I meant implementing this dataclass-dataclass factory solution. I looked up the library.

celest charm
#

well, I'm not sure other people would approve of that

#

after all, that's an extra dependency

molten perch
#

Yes, valid point..

celest charm
#

and sir-lancebot is a bot for adding fun features, not proving theorems at compile time or something

#

so it's more or less fine to write in a jesus-take-the-wheel style

reef tinsel
#

Hi! I am working on bot#1765

I a getting an error while trying to get a role object for a certain role.

I am using

role = discord.Role(id = id)

However this causing an error, presumably because that's not the way to get a role object from an id?

What should I use?

dusky shoreBOT
static canyon
#

Yeah, you don't get roles like that

#

You do guild.get_role(id)

reef tinsel
static canyon
#

And if you don't have guild then you get that from self.bot.get_guild(constants.Guild.id)

#

You'll also likely need to make sure neither of those are None

reef tinsel
#

thank you!

static canyon
#

Since if they aren't in cache they'll both return None

reef tinsel
static canyon
#

If you 100% need them then you can do get or fetch

#

i.e role = guild.get_role(id) or await guild.fetch_role(id)

reef tinsel
#

ok

static canyon
#

Just don't do that when getting Users

vale ibex
#

as long as you add await self.bot.wait_until_guild_available() at the start, you should be able to get the roles just fine

reef tinsel
#

i only need to get the roles - so the user problem shouldn't be a proble

vale ibex
#

since that ensures the role, member and channel caches are populated

brazen charm
static canyon
#

mypy doesn't allow for x = type from the looks of it

Or rather, mypy doesn't understand that it's saying "interpret x as y"

dim pelican
#

@vocal wolf congrats on that monster PR

vocal wolf
#

hell I didn't squash

#

I swear I pressed it

brazen charm
static canyon
brazen charm
static canyon
#

Hmm

#

I get what you mean

brazen charm
#

I'm not familiar with mypy and not that experienced with typing overall so there could be some way around that

static canyon
#

It does work but definitely not clean

#

I think subclassing the converters also works but again, not nice

brazen charm
#

The initial approach I had in mind with monkeypatching dpy to understand Annotated should work but I don't think we want to change to that unless it's decided that the bot will use mypy (and there isn't a nicer way with the TYPE_CHECKING)

static canyon
#

I'll leave these errors for last then

fallen patrol
#

I think that because pydis uses pytest to run the tests its fine

#

pytest actually rewrites all assert statements

dim pelican
fallen patrol
#

ah, idk πŸ€·β€β™€οΈ

static canyon
#

Does anyone know how to make mypy recognise log.trace as valid? It currently gives error: "Logger" has no attribute "trace" and this isn't exactly an error we can specify to ignore by adding # ignore [attr-defined] after each call

CC @celest charm since you were helping with mypy earlier

celest charm
#

I have no idea

#

maybe make custom stubs for logging? idk how that works

static canyon
#

Hmm interesting idea

brisk brook
#

So instead of adding sanity checks with it statements and raising errors, use asserts

static canyon
brisk brook
#

Sanity checks (if I used the term correctly) is just checks you put in your code to make sure things are as you expect them. Like after a complicated operation you expect certain lists to be empty, etc. You can use asserts there which will raise an error if it isn't the case.

gritty wind
#

Possibly importing the logger, or using the one on the bot instance

#

Not sure what’ll happen to the names then though

#

Maybe you could define a custom logger class which just inherits logger, and adds grace (I think we already do this)

#

Anyways, none of this is probably relevant since we were considering removing trace all together

#

Where did we fall on that

brazen charm
#

I think it is useful, and can now be used properly with the loggers being controlled through the env var

static canyon
#

As much as removing it would make life easy, I do think it's useful

static canyon
#

And that will still have the same issue anyway apparently (according to the article I linked above)

fallen patrol
#

Sadly.

#

and even so, even if you do set a logger class, mypy will still complain because the logging.getLogger method is typed wrongly

#

but there is this

#

!d logging.setLoggerClass

stable mountainBOT
#

logging.setLoggerClass(klass)```
Tells the logging system to use the class *klass* when instantiating a logger. The class should define `__init__()` such that only a name argument is required, and the `__init__()` should call `Logger.__init__()`. This function is typically called before any loggers are instantiated by applications which need to use custom logger behavior. After this call, as at any other time, do not instantiate loggers directly using the subclass: continue to use the [`logging.getLogger()`](https://docs.python.org/3.10/library/logging.html#logging.getLogger "logging.getLogger") API to get your loggers.
fallen patrol
#

so you can subclass logging.Logger and all logger instances will be of that class

#

--

#

(why the fuck is it klass)

brazen charm
#

because class is invalid

patent pivot
#

yeah klass or _class are quite common substitutes

static canyon
fallen patrol
brisk brook
#

Can you import that logger class and always do log: MyLogger = logger.getlogger(__name__)?

fallen patrol
#

yeah, that's what I do

#

however

#

mypy will still complain about that assignment statement

static canyon
#

I mean it's a one off thing so we can just # ignore imo

fallen patrol
static canyon
#

Potentially, see bot#1773

dusky shoreBOT
static canyon
#

Found 1423 errors in 100 files (checked 128 source files)
"fun" is not the right word

#

mhm

#

That's what I'm doing lol

#

I've gotten a fair amount of things worked out

#

But there's still big roadblocks, like the fact we have a custom log.trace which gives an error every time it's used

static canyon
fallen patrol
#

i sent it in my dev channel on my server lmao

#

so, I don't use mypy as of right now, but codewise

#
# modmail/__init__.py
from modmail.log import ModmailLogger
logging.TRACE = 5
logging.NOTICE = 25
logging.addLevelName(logging.TRACE, "TRACE")
logging.addLevelName(logging.NOTICE, "NOTICE")
``````py
# modmail/log.py in full
import logging
from typing import Any


class ModmailLogger(logging.Logger):
    """Custom logging class implementation."""

    def trace(self, msg: Any, *args, **kwargs) -> None:
        """
        Log 'msg % args' with severity 'TRACE'.

        To pass exception information, use the keyword argument exc_info with
        a true value, e.g.

        logger.trace("Houston, we have a %s", "low-level problem", exc_info=1)
        """
        self.log(logging.TRACE, msg, *args, **kwargs)

    def notice(self, msg: Any, *args, **kwargs) -> None:
        """
        Log 'msg % args' with severity 'NOTICE'.

        To pass exception information, use the keyword argument exc_info with
        a true value, e.g.

        logger.notice("Houston, we have a %s", "not-quite-a-warning problem", exc_info=1)
        """
        self.log(logging.NOTICE, msg, *args, **kwargs)
#

Those docstrings were taken directly from the library in order to keep consistency between custom levels and built in levels

#

In retrospec, I would probably move the stuff from init.py to modmail/log.py

static canyon
#

Where do you use setLoggerClass?

#

.gh repo discord-modmail/modmail

dusky shoreBOT
#

A Modmail bot for Discord. Allowing safe moderator conversations with server members one server at a time.

fallen patrol
static canyon
#

ty

gritty wind
#

Here is a working solution that doesn't require humdereds of ignors:

#
import logging
import typing

logging.addLevelName(5, "trace")


class Logger(logging.Logger):
    def trace(self: logging.Logger, msg: str, *args, **kwargs) -> None:
        """
        Log 'msg % args' with severity 'TRACE'.

        To pass exception information, use the keyword argument exc_info with
        a true value, e.g.

        logger.trace("Houston, we have an %s", "interesting problem", exc_info=1)
        """
        if self.isEnabledFor(5):
            self._log(5, msg, args, **kwargs)


if typing.TYPE_CHECKING:
    def get_logger(name: str = None) -> Logger:
        return logging.getLogger(name)  # type: ignore

    logging.getLogger = get_logger


if __name__ == '__main__':
    logging.setLoggerClass(Logger)
    logging.getLogger(__name__).trace("test")

#

That trace function is ripped straight from the bot, it isn't new code

#

The hack in the type_checking block is factually correct

fallen patrol
#

but now you have to use a custom get_logger method?

gritty wind
#

No?

#

logging.setLoggerClass(Logger)

fallen patrol
#

oh wait

#

aside from hacky I kind of like it πŸ‘€

gritty wind
#

It should require no other code changes

static canyon
#

Nice

#

Where do I copy-paste this into?

gritty wind
#

One sec, I'll get you a patch lol

fallen patrol
#

I don't get how this works: ( see my comment )

if typing.TYPE_CHECKING:
    # this method isn't the same name as getLogger
    def get_logger(name: str = None) -> Logger:
        return logging.getLogger(name)  # type: ignore

    logging.getLogger = get_logger
gritty wind
#

It doesn't have to be

#

It's just a pointer

fallen patrol
#

??

gritty wind
#

What matters to mypy here is that the annotated return of logging.getLogger is now the custom logger class

static canyon
#

yeah

fallen patrol
#

ah

#

i wonder if this could get into the stdlib lol

#

and use a typevar on the return of getLogger

tough imp
static canyon
#

I think that's what logging.setLoggerClass(Logger) is for

tough imp
#

well it works but it doesn't fool the type checker is what I mean πŸ˜…

static canyon
#

Then each time you do logger: Logger = logging.getLogger(__name__), and this has the custom class so will work

tough imp
#

wouldn't it be nicer to have a custom log_utils.get_logger func that wraps logging.getLogger?

#

and overrides the return type

#

then import that everywhere instead of logging.getLogger

#

and you get rid of the camel case too 😌

static canyon
#

So you're suggesting logger = log_utils.get_logger(__name__) instead of logger: bot.log.Logger = logging.getLogger(__name__)?

tough imp
#

yeah

#

just seems like less trouble

static canyon
#

hmm

gritty wind
#

provided this is loaded before them

tough imp
#

yeah it will work in the sense that you'll get your logger

#

but a type checker wouldn't see the if TYPE_CHECKING block from another module, right

gritty wind
#

Hmm it feels like it should

#

Let me test a bit

tough imp
#

I guess it also depends on which type checker is being used

static canyon
#

mypy is the one we're thinking of adding

gritty wind
#

Yeah mypy had no problem

#

Provided I add this to Init

trim cradle
#

how does one run the site tests in the docker container?

gritty wind
#

The type checking block

trim cradle
#

sudo docker exec 5a3e85b3e32e "poetry run task lint" did not work

tough imp
#

ah that surprises me

static canyon
gritty wind
#

Let me get this uploaded somewhere you can play with it

tough imp
#

I've never really used mypy though so maybe I'm underestimating it

gritty wind
#

What supports multi file code

#

I could always push my finalized solution to a branch

static canyon
gritty wind
#

Well, wait a minute. I have a __main__, __init__, and a regular python file in a module. I told mypy to run as a module, but it's saying it found 1 file

static canyon
#

lol

gritty wind
#

Did it actually lint everything or not is the Q

static canyon
#

If it says 1 source file it only checked that one file

gritty wind
#

Well, you're right it'll be check

#

How would I get it running over a module

#

It says -m is an option

#

But that seems false

static canyon
#

mypy -m x.py I think?

gritty wind
#

-m MODULE, --module MODULE Type-check module; can repeat for more modules

#

That is what I used hmm

#

package maybe

#

Ah that's done it

#

Okay, sadly didn't work

static canyon
#

f

gritty wind
#

That is a nice solution

static canyon
#

mhm

#

That's probs what I'd do (the first of the two)

gritty wind
#

That's the price we pay for staticness

patent pivot
#

more metrics for the Joe Banks Gobblerℒ️

brazen charm
trim cradle
#

ooo something's happening

stable mountainBOT
#

review-policies/core-developers.yml lines 102 to 103

- ".github/workflows/*"
- "Dockerfile"```
fallen patrol
#

should pyproject.toml be a part of this or no?

patent pivot
#

hmmmmm

#

nah

fallen patrol
#

most of the time, it would be removing or adding a dependency

patent pivot
#

because then every PR that adds a dep needs a sign off from devops

fallen patrol
#

ah

#

ok

patent pivot
#

if people feel it needs an explicit devops review they can add the label

fallen patrol
#

huh

#

does that mean that docker-compose.yml is not used when deploying any of pydis's stuff?

#

since presumably if it was used, it would be on that list

brazen charm
#

That's for dev

gritty wind
#

@static canyon @tough imp I found a solution that does work for all files in a module, but this is more of a curiosity than something that wouldn't get your head decapitated:

In a logging.pyi, I added:

from logged import CustomLogger

def getLogger(name: str = None) -> CustomLogger:
    ...

This ofc raised errors when I tried using anything else in the logging library, but to get around that, you can use (this is in __init__.py):

from Lib import logging
logging.something_not_in_stub()

and nothing will complain anymore. Other files can use the normal import logging with no problem

#

Again, don't suggest this

#

HIGHLY ILLEGAL

fervent sage
#

since all of pydis' stuff is on k8s, compose files will never be used in prod

patent pivot
tough imp
#

i dont follow

#

you called a function that doesnt exist and it solved the problem?