Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
CarForumPoster
Jun 26, 2013

⚡POWER⚡

SurgicalOntologist posted:

I guess I mean, whatever you're doing in the cell, its causing one request and another to not actually be the same despite appearing to have the same parameters. The actual request you're sending might be different, in some way, depending on the state of all that code you're running in that cell. It's the only way I can make sense of it.

Yeah, but what I'm suggesting is taking that "r" object you get, specifically r.requests which is an object of type requests.PreparedRequest that encapsulates all the information you're sending to the server, and take a close look. See if r.request and r2.request are different in any way, assuming r is a failed response and r2 is a successful one. Are the request headers exactly the same? Is the request body exactly the same?

To put it another way, if you send the same exact set of bytes to the server, it shouldn't matter whether you send it from one cell or another (given that you seem to have ruled out rate-limiting on the server side with your sleep experiment). Much more likely than anything else I can think of, you are not actually sending the same bytes, but somehow the context of the cell (i.e. the loop) is changing what bytes you send to the server. Inspecting your PreparedRequest will help you figure out if that's the case. Also, other fields of the response (r) itself might be interesting too, perhaps there is a clue in the status code or the headers returned from the server.

Aye! dem potent response

Adbot
ADBOT LOVES YOU

Josh Lyman
May 24, 2009


SurgicalOntologist posted:

I guess I mean, whatever you're doing in the cell, its causing one request and another to not actually be the same despite appearing to have the same parameters. The actual request you're sending might be different, in some way, depending on the state of all that code you're running in that cell. It's the only way I can make sense of it.

Yeah, but what I'm suggesting is taking that "r" object you get, specifically r.requests which is an object of type requests.PreparedRequest that encapsulates all the information you're sending to the server, and take a close look. See if r.request and r2.request are different in any way, assuming r is a failed response and r2 is a successful one. Are the request headers exactly the same? Is the request body exactly the same?

To put it another way, if you send the same exact set of bytes to the server, it shouldn't matter whether you send it from one cell or another (given that you seem to have ruled out rate-limiting on the server side with your sleep experiment). Much more likely than anything else I can think of, you are not actually sending the same bytes, but somehow the context of the cell (i.e. the loop) is changing what bytes you send to the server. Inspecting your PreparedRequest will help you figure out if that's the case. Also, other fields of the response (r) itself might be interesting too, perhaps there is a clue in the status code or the headers returned from the server.
This kinda makes sense to me. Stupid question, how do I run requests on a local server? The API call work like
Python code:
from localserver import localpackage
from localserver.localpackage.parameters import queryname
%env no_proxy.com=.localserver.com,localhost

my code loop
	localpackage.queryname(param1, param2...)
In a browser window, I go to http://jupyterlab.localserver.com/user/username/lab which has a one-click AWS Cognito login. The opens JupyterLab where I run all my notebooks. The same server also handles the API call (I think). Since I'm using any URLs or user/pass for the API call, I'm not sure how to get requests to work with this setup.

Josh Lyman fucked around with this message at 15:35 on Aug 20, 2022

Falcon2001
Oct 10, 2004

Eat your hamburgers, Apollo.
Pillbug
I have a weird question. I inherited a codebase that's got an interesting decision in it and I'm trying to decide whether to start reverting it.

Python code:
Area = Enum("Area", get_all_valid_Areas_dict(), type=str)

# example return from get_all_valid_Areas_dict():
{
	"ABC": "abc", 
	"XYZ": "xyz"
}
Edit: get_all_valid_areas_dict comes from a library we ingest that defines area data, so building the list dynamically is the right thing to do.

I'm not a CS major so I might be missing something, but my understanding of enums is that it's used to define a finite list of things in code, not in your data. It seems like if we're building the list dynamically we should just store it as a list/set of valid strings?

My biggest problem with it is that because of this, you can create this enum in two ways:


Python code:
r = Area['XYZ']
r2 = Area('xyz')
r == r2
Which has lead to a few bugs already due to misunderstanding whether we're working with upper or lowercase

Is this as dumb as I think it is?

Falcon2001 fucked around with this message at 18:49 on Aug 20, 2022

QuarkJets
Sep 8, 2008

Falcon2001 posted:

I have a weird question. I inherited a codebase that's got an interesting decision in it and I'm trying to decide whether to start reverting it.

Python code:
Area = Enum("Area", get_all_valid_Areas_dict(), type=str)

# example return from get_all_valid_Areas_dict():
{
	"ABC": "abc", 
	"XYZ": "xyz"
}
Edit: get_all_valid_areas_dict comes from a library we ingest that defines area data, so building the list dynamically is the right thing to do.

I'm not a CS major so I might be missing something, but my understanding of enums is that it's used to define a finite list of things in code, not in your data. It seems like if we're building the list dynamically we should just store it as a list/set of valid strings?

My biggest problem with it is that because of this, you can create this enum in two ways:


Python code:
r = Area['XYZ']
r2 = Area('xyz')
r == r2
Which has lead to a few bugs already due to misunderstanding whether we're working with upper or lowercase

Is this as dumb as I think it is?

That's a reasonable way to define an enum, it's why the functional interface exists

I think what you and some members of your team may not understand is that enum members are singletons. Area('xyz') is not creating a new Area instance, it's returning the member of Area that corresponds to value 'xyz'. Not only are those two variables equal, they're exactly the same object:

Python code:
r = Area['XYZ']  # Access Area member by member name
r2 = Area('xyz')  # Access Area member by member value
r == r2  # True
r is r2  # True, both variables point to the same object: Area.XYZ
r is Area.XYZ  # True
Since the members are all singletons they have the usefulness of global variables but are much safer to use. Once defined, an enum's mapping cannot change:
Python code:
Area.XYZ = 'abcd'  # An exception is raised, members cannot be reassigned
And if you want multiple members to map to the same value, they'll point to the same singleton:
Python code:
x = {'ABC': 'abc', 'XYZ': 'xyz', 'DEF': 'xyz'}
Area = Enum("Area", x)
Area.XYZ is Area.DEF  # True; members with the same value are the same
Area.XYZ is Area.ABC  # False; these members had different values
Since an Enum is a class like any other, you can attach helpful methods to it that every member will then receive.
Python code:
def contains_x(self):
    """Test if the char 'x' is present."""
    return 'x' in self

Area.contains_x = contains_x

Area.XYZ.contains_x()  # True; the value contains "x"
Area['XYZ'].contains_x()  # True - remember, this is accessing XYZ by member name, but the evaluation is against the value
Area.ABC.contains_x()  # False
Since your enum is using the "type" argument and setting it to "str", your values already contain all of the standard string methods. So if you're ever unsure whether an instance has an uppercase or lowercase value, just use a string method on it:
Python code:
# Some code that returned an Area member, doesn't really matter what it is
some_area = Area.XYZ
some_area.islower()  # True; the value was "xyz", which is lowercase
some_area.isupper()  # False
If you ever need the name of one of these singletons, you get that with the "name" attribute
Python code:
Area.XYZ.name.isupper()  # True
Area['XYZ'].name.isupper()  # True; we accessed Area.XYZ by name, then asked if its name ("XYZ") was uppercase
Area('xyz').name.isupper()  # True; we accessed Area.XYZ by value, then asked if its name ("XYZ") was uppercase

QuarkJets fucked around with this message at 22:50 on Aug 20, 2022

Deffon
Mar 28, 2010

Falcon2001 posted:

I have a weird question. I inherited a codebase that's got an interesting decision in it and I'm trying to decide whether to start reverting it.

Python code:
Area = Enum("Area", get_all_valid_Areas_dict(), type=str)

# example return from get_all_valid_Areas_dict():
{
	"ABC": "abc", 
	"XYZ": "xyz"
}
Edit: get_all_valid_areas_dict comes from a library we ingest that defines area data, so building the list dynamically is the right thing to do.

I'm not a CS major so I might be missing something, but my understanding of enums is that it's used to define a finite list of things in code, not in your data. It seems like if we're building the list dynamically we should just store it as a list/set of valid strings?

My biggest problem with it is that because of this, you can create this enum in two ways:


Python code:
r = Area['XYZ']
r2 = Area('xyz')
r == r2
Which has lead to a few bugs already due to misunderstanding whether we're working with upper or lowercase

Is this as dumb as I think it is?

You are typically expected to define enum members by hand.
As long as "get_all_valid_Areas_dict" is reliable, easy to understand, and never removes keys over time, it might not be a problem. It's especially defensible if the alternative is having to update multiple places when a new area is added, and if you need to add areas frequently.
Your IDE (and probably mypy) will not be able to distinguish between valid and invalid enum members in the code, so that is a bummer.
If you need a few valid operations for areas, you can add methods for them, which is more confortable if you use the class-based syntax (class Area(Enum): ...) instead of the functional syntax Area=Enum(...). Using the class-based syntax means abandoning the easy way to dynamically create the enum unfortunately.


The pitch for enums is that you provide more clarity to your domain and code by being more specific. An area e.g. is not something that you can do arbitrary string operations on, and the result of those operations are not necessarily areas themselves. People reading your code will understand that it doesn't represent e.g. an unverified freetext field that a user can write anything they want in.
It also a "proof" of validation - if your function only works with valid areas, then the fact that the caller was able to supply an Area member means that it's already validated. If you only use strings, then it's easy to send unvalidated areas to functions that expect validated ones, so every function that expect validated areas has to do an extra round of validation to help prevent mistakes.

Dynamically typed languages like Python don't reap the benefits from wrapper types (in this case wrapping string values) as much as statically typed languages do.
Unless you use type annotations you need a way to distinguish between parameters that are meant to follow the Area protocol (comparable with other Areas, has a "value" and "name" properties) and the string protocol (has contains(), startswith() etc. methods).
It's easy to confuse what something is supposed to be without a naming convention like "blah_area" and "blah_area_str". Even so, the caller might send the wrong thing, especially if they don't use keyword arguments.

You should only convert enums from and to strings when communicating with the user and with apis outside your control that only understand strings.
In the rest of the code you should refer to hardcoded enum members like so "Area.XYZ".
That way it's harder to confuse if you working with enums or their string counterparts.

In short, if you are able to apply certain conventions then enums can greatly benefit your code.

Falcon2001
Oct 10, 2004

Eat your hamburgers, Apollo.
Pillbug
Thanks to both of you for context! I think the problem is that we don't ever refer to a single area in code; it's only used for validation of things like 'Ah, this input file has an area, let me see if it's a valid one'. The closest we get in being able to refer to a passed in variable as an Area instead of just str.

Deffon posted:

The pitch for enums is that you provide more clarity to your domain and code by being more specific. An area e.g. is not something that you can do arbitrary string operations on, and the result of those operations are not necessarily areas themselves. People reading your code will understand that it doesn't represent e.g. an unverified freetext field that a user can write anything they want in.
It also a "proof" of validation - if your function only works with valid areas, then the fact that the caller was able to supply an Area member means that it's already validated. If you only use strings, then it's easy to send unvalidated areas to functions that expect validated ones, so every function that expect validated areas has to do an extra round of validation to help prevent mistakes.

Dynamically typed languages like Python don't reap the benefits from wrapper types (in this case wrapping string values) as much as statically typed languages do.
Unless you use type annotations you need a way to distinguish between parameters that are meant to follow the Area protocol (comparable with other Areas, has a "value" and "name" properties) and the string protocol (has contains(), startswith() etc. methods).
It's easy to confuse what something is supposed to be without a naming convention like "blah_area" and "blah_area_str". Even so, the caller might send the wrong thing, especially if they don't use keyword arguments.

You should only convert enums from and to strings when communicating with the user and with apis outside your control that only understand strings.
In the rest of the code you should refer to hardcoded enum members like so "Area.XYZ".
That way it's harder to confuse if you working with enums or their string counterparts.

In short, if you are able to apply certain conventions then enums can greatly benefit your code.

This part makes me think it might be worth keeping this around, and just removing the case discrepancy between their names and values, so we always interact with them using the same casing, which would remove some of the weirdness from interacting with them.


QuarkJets posted:

That's a reasonable way to define an enum, it's why the functional interface exists

I think what you and some members of your team may not understand is that enum members are singletons. Area('xyz') is not creating a new Area instance, it's returning the member of Area that corresponds to value 'xyz'. Not only are those two variables equal, they're exactly the same object:
<more words>

Thanks! I didn't actually realize they were singletons, so that's handy to know.

QuarkJets posted:

Since an Enum is a class like any other, you can attach helpful methods to it that every member will then receive.
Python code:
def contains_x(self):
    """Test if the char 'x' is present."""
    return 'x' in self

Area.contains_x = contains_x

Area.XYZ.contains_x()  # True; the value contains "x"
Area['XYZ'].contains_x()  # True - remember, this is accessing XYZ by member name, but the evaluation is against the value
Area.ABC.contains_x()  # False

This part, however, I'm reasonably confident this gets a lot messier if we're defining the enum dynamically. I looked into using this before using the class-style declaration and it looks like you have to do a bunch of subclassing. https://stackoverflow.com/questions/43096541/a-more-pythonic-way-to-define-an-enum-with-dynamic-members I think is what I found before.

Overall, It sounds like using enums to basically be a clarity/validation point isn't a terrible idea (seeing a method that calls for an Area is clearer than a str), but the fundamental problem we're running into is that of the casing confusion - since you can interact with the enum using either a lower or uppercase, and the difference between () and [] is subtle enough to skip notice sometimes.

I'll look into what we gain by standardizing on one case, and then doing .upper() or .lower() whenever we have to write out/etc and see if that helps.

QuarkJets
Sep 8, 2008

Your internal code should use Area members directly, e.g. Area.XYZ. Your external interfaces should convert input strings to Areas.

Falcon2001
Oct 10, 2004

Eat your hamburgers, Apollo.
Pillbug

QuarkJets posted:

Your internal code should use Area members directly, e.g. Area.XYZ. Your external interfaces should convert input strings to Areas.

Ninja edit: I got most of the way through writing a big explanation of my service but I think I just am agreeing with you. I think there's just a bunch of old code lying around that does some odd things, so I'm trying to prioritize what I can bother rewriting...especially in a codebase with like 30% test coverage, which makes refactors a little scary. (I'm working on that last part, but thanks to a bunch of module variables and singleton patterns it's not an easy thing to fix.)

Bad Munki
Nov 4, 2008

We're all mad here.


Falcon2001 posted:

...especially in a codebase with like 30% test coverage, which makes refactors a little scary.

Out of curiosity, what’s usually considered good coverage? We’re at 88% on our most recent release of a certain library and we’re trying to decide if we should formally set a minimum and, if so, what it should be. Assuming 100% is the dream, not the expectation.

Jabor
Jul 16, 2010

#1 Loser at SpaceChem
Goodhart's law applies here - the moment you make code coverage an actual target instead of just something that you look at to figure out what part of the codebase might need more testing, you're going to wind up with a whole bunch of pointless change-detector tests that exercise all the code paths without actually testing the behaviour you're interested in.

QuarkJets
Sep 8, 2008

Jabor posted:

Goodhart's law applies here - the moment you make code coverage an actual target instead of just something that you look at to figure out what part of the codebase might need more testing, you're going to wind up with a whole bunch of pointless change-detector tests that exercise all the code paths without actually testing the behaviour you're interested in.

Even so, it's important to set a threshold for low coverage because

1) There will also be developers who use the notification of low coverage to develop good new tests

2) Smoke testing is better than no testing, and for some lines it's actually all that's needed

In a world where most codebases have no testing, the appropriate coverage is a decision to be agreed upon between the developers and the managers. If you're a person who can influence this, get it as high as possible; 80% would be a reasonable threshold. Setting it too high risks what Jabor is describing

QuarkJets fucked around with this message at 18:18 on Aug 21, 2022

Bad Munki
Nov 4, 2008

We're all mad here.


Yeah, we got 88% without worrying about the number and just targeting good tests and discovered-in-practice edge cases and such for regression, and my gut tells me that’s a pretty good number to have hit with that approach. Of course it would also require, in the event of a low number, looking at WHAT code isn’t getting covered, and whether we actually care.

Just curious what other people might use as a “sensible” jumping off point. I guess I could look at some other Big Kid libraries and see how they’re doing.

The primary goal is to have very high confidence in regression testing. New feature testing second to that.

Absolutely agree that just saying “95% no matter what” or similar is potentially very counterproductive. And maybe it’s best to just not set an actual number and instead focus almost exclusively on occasionally examining what, specifically, is being missed.

SurgicalOntologist
Jun 17, 2004

Josh Lyman posted:

This kinda makes sense to me. Stupid question, how do I run requests on a local server? The API call work like
Python code:
from localserver import localpackage
from localserver.localpackage.parameters import queryname
%env no_proxy.com=.localserver.com,localhost

my code loop
	localpackage.queryname(param1, param2...)
In a browser window, I go to http://jupyterlab.localserver.com/user/username/lab which has a one-click AWS Cognito login. The opens JupyterLab where I run all my notebooks. The same server also handles the API call (I think). Since I'm using any URLs or user/pass for the API call, I'm not sure how to get requests to work with this setup.

I have no idea what you mean by this localserver thing and Google isn't helping. I assumed from previous posts that you were using requests. If that's not the case, my specific advice is less relevant but probably the answer lies in this direction. Is it really running on localhost? Do you control the server? Where did the code for localserver.localpackage come from?

This makes it more complicated because the state that's affecting the requests might be coming from your code but from the code you're importing. In which case there might not be much you can do about it. Whether its possible to use requests instead depends on how much info you can get about the actual HTTP API and the complexity of the code (you can see the code in Jupyterlab by typing the function/class/module with ?? after). Probably in the code they are using requests but not necessarily. I mean, with the info we've seen, maybe it's not even an HTTP API but a simulation of one, in which case all bets are off.

Mycroft Holmes
Mar 26, 2010

by Azathoth
Getting back into python after a few years, so I'm rusty. Doing a user input validation and its throwing up error, haven't run it yet though.

code:
    def lab(self):
        print("Please input lab grade: ")
        labs = input()
        if isinstance(labs, (float, int)):
            return labs
        else:
            print("Incorrect input. Please enter a number.")
            labs = input()

Phobeste
Apr 9, 2006

never, like, count out Touchdown Tom, man

Mycroft Holmes posted:

Getting back into python after a few years, so I'm rusty. Doing a user input validation and its throwing up error, haven't run it yet though.

code:
    def lab(self):
        print("Please input lab grade: ")
        labs = input()
        if isinstance(labs, (float, int)):
            return labs
        else:
            print("Incorrect input. Please enter a number.")
            labs = input()

input() will always return a string, you've gotta try and convert it yourself, like

code:
def lab(self):
    print("Please input lab grade: ")
    labs = input()
    try:
        grade = float(labs)
    except ValueError: # what float(str) raises if it fails
        print("you can't give a lab an a what are you thinking")
also if you want to be trying again it probably wants to be in a loop

Jigsaw
Aug 14, 2008

Mycroft Holmes posted:

Getting back into python after a few years, so I'm rusty. Doing a user input validation and its throwing up error, haven't run it yet though.

code:
    def lab(self):
        print("Please input lab grade: ")
        labs = input()
        if isinstance(labs, (float, int)):
            return labs
        else:
            print("Incorrect input. Please enter a number.")
            labs = input()
Two other questions that seem relevant:

What gets returned if there’s a single invalid input?

What happens if there’s an invalid input twice in a row?

Without seeing more context, it’s hard to know what the right answers to these are (or whether they’re already answered somewhere outside that function). But probably you want a while loop.

Mycroft Holmes
Mar 26, 2010

by Azathoth
so, something like this is better?

code:
import calc


class Main:

    def __init__(self):
        self.labs = "A"
        self.midterm = "a"
        self.final = "a"

    def lab(self):
        while labs is not int:
            print("Please input lab grade: ")
            labs = input()
            try:
                grade = float(labs)
            except ValueError:  # what float(str) raises if it fails
                print("Invalid input.")
                labs = input()

Phobeste
Apr 9, 2006

never, like, count out Touchdown Tom, man

Mycroft Holmes posted:

so, something like this is better?

code:
import calc


class Main:

    def __init__(self):
        self.labs = "A"
        self.midterm = "a"
        self.final = "a"

    def lab(self):
        while labs is not int:
            print("Please input lab grade: ")
            labs = input()
            try:
                grade = float(labs)
            except ValueError:  # what float(str) raises if it fails
                print("Invalid input.")
                labs = input()

well, take a pass at that and just tell yourself what it does for two iterations in the loop - or hell, just run it.

one thing that might not be immediately obvious is that it should be while not isinstance(labs, whatever) not labs is not whatever

Jigsaw
Aug 14, 2008

Mycroft Holmes posted:

so, something like this is better?

code:
import calc


class Main:

    def __init__(self):
        self.labs = "A"
        self.midterm = "a"
        self.final = "a"

    def lab(self):
        while labs is not int:
            print("Please input lab grade: ")
            labs = input()
            try:
                grade = float(labs)
            except ValueError:  # what float(str) raises if it fails
                print("Invalid input.")
                labs = input()

To expand on Phobeste's comment, the reason to not use "is" to check types is because it checks for identity and not class (i.e., type) membership. So your conditional is literally asking if labs is the same thing as the type int, not if it is of the type int. You want the latter, which is what isinstance does.

Why is self.labs a string ("A"), when the lab method is getting a float? If you're converting a float to a string letter grade, that probably ought to be handled inside the lab method.

Do you want the lab method return a value, or to set the value of an attribute? If you want the latter (which seems more likely), you'll need to use self.labs inside the lab method. If you want the former for some reason, you'll need to make sure to have a return statement somewhere inside the method. (Here's a related question: why is lab a method of the Main class and not a standalone function? Currently it doesn't create or modify any attributes of the Main class.)

Less particular to your situation, but some of these questions would be easier to help with if you used a docstring for the lab method, so it was clearer what exactly it should do. In general, every function/method should have a docstring (though I'd usually consider it understandable to leave it out when asking questions somewhere like here).

Mycroft Holmes
Mar 26, 2010

by Azathoth
ok, getting a bunch of different errors.

It eternally loops if a numerical value is put in. it throws an error if a letter is put in, despite the valueerror bit.

code:
import calc


class Main:

    def __init__(self):
        self.labs = 0
        self.midterm = 0
        self.final = 0

    def lab(self):
        print("Please input lab grade: ")
        labs = input()
        grade1 = float(labs)
        while labs is not isinstance(labs, (float, int)):
            try:
                grade1 = float(labs)
            except ValueError:  # what float(str) raises if it fails
                print("Invalid input.")
                labs = input()
        return grade1

    def mid(self):
        print("Please input midterm grade: ")
        midterm = input()
        while midterm is not isinstance(midterm, (float, int)):
            print("Please input midterm grade: ")
            midterm = input()
            try:
                grade2 = float(midterm)
            except ValueError:  # what float(str) raises if it fails
                print("Invalid input.")
                midterm = input()
        return grade2

    def fin(self):
        print("Please input final grade: ")
        final = input()
        while final is not isinstance(final, (float, int)):
            print("Please input final grade: ")
            final = input()
            try:
                grade3 = float(final)
            except ValueError:  # what float(str) raises if it fails
                print("Invalid input.")
                final = input()
        return grade3


def main():
    a = Main()
    grade1 = a.lab()
    grade2 = a.mid()
    grade3 = a.fin()
    total = calc.average(grade1, grade2, grade3)
    print("Your grade is " + total)


if __name__ == "__main__":
    main()

boofhead
Feb 18, 2021

Phone posting but

The while loop checks labs but what you're converting to a float is grade1. So labs will only ever be a string as that's the input var

And you're trying to convert the input to a float outside the try except so it doesn't have error handling on invalid inputs, that's where the error is coming from

Edward IV
Jan 15, 2006

The line where you're trying to cast the input as a float outside of the try block is what's throwing the value error.

Basically, you don't need the first instance of this line in the lab function
code:
grade1 = float(labs)
Your midterm and finals function does appear to be correct.

As for the infinite looping, your while function should only need the isinstance function since that will return a boolean and the "is" comparison isn't needed. Since the successfully casted float and the result of the isinstance function are different objects, the while loop condition will always resolve as true hence the infinite looping and is prevalent in all your grade functions.

The function should look like this:

code:
def lab(self):
        print("Please input lab grade: ")
        labs = input()
        while not isinstance(labs, (float, int)):
            try:
                grade1 = float(labs)
            except ValueError:  # what float(str) raises if it fails
                print("Invalid input.")
                labs = input()
        return grade1
EDIT: Ok hang on what I have with the while loop condition isn't quite right as mentioned above.

EDIT 2: Infinite loops may not be ideal but the following could work since a successful cast to float should mean the input was a number which the function will return and break out of the loop.

code:
def lab(self):
        print("Please input lab grade: ")
        while True:
            try:
		return float( input() )
            except ValueError:  # what float(str) raises if it fails
                print("Invalid input.")
                

Edward IV fucked around with this message at 15:23 on Aug 22, 2022

Josh Lyman
May 24, 2009


SurgicalOntologist posted:

I have no idea what you mean by this localserver thing and Google isn't helping. I assumed from previous posts that you were using requests. If that's not the case, my specific advice is less relevant but probably the answer lies in this direction. Is it really running on localhost? Do you control the server? Where did the code for localserver.localpackage come from?

This makes it more complicated because the state that's affecting the requests might be coming from your code but from the code you're importing. In which case there might not be much you can do about it. Whether its possible to use requests instead depends on how much info you can get about the actual HTTP API and the complexity of the code (you can see the code in Jupyterlab by typing the function/class/module with ?? after). Probably in the code they are using requests but not necessarily. I mean, with the info we've seen, maybe it's not even an HTTP API but a simulation of one, in which case all bets are off.
I was able to fix my issue but thanks for your help.

As an unrelated question, np.argmin() isn't behaving the way I'm expecting. Say I have an array [200,500,1000]. np.argmin(myArray>=100) should return 1 right? >=500 should return 2, not sure what >=5000 should return. What am I missing? Isn't it the minimum index (starting at 1) that satisfies the inequality?

edit: I think I have it figured out. np.argmin() returns the index of the lowest value, and the inequality return an array of True/False, so it's actually checking for the minimum index (starting at 0) that's False?

Josh Lyman fucked around with this message at 18:03 on Aug 23, 2022

Zoracle Zed
Jul 10, 2001
that's correct. I think you're looking for np.where(myArray>=100)[0]

Mycroft Holmes
Mar 26, 2010

by Azathoth
Ok, I need to have a program exit if the user presses enter without inputting data. Is there a way to detect a blank field?

Jigsaw
Aug 14, 2008

Mycroft Holmes posted:

Ok, I need to have a program exit if the user presses enter without inputting data. Is there a way to detect a blank field?

code:
import sys
response = input("Enter something: ")
if not response:
     print("No input. Exiting.")
     sys.exit(0)

# input handling goes here
Depending on what exactly you want to happen, you could alternatively check for an input and handle it if found (just get rid of "not" in the conditional), and/or raise a ValueError if no input is provided (which could be caught and handled in some cases by other code—the above is different since it will exit regardless, so it depends on what you want).

CarForumPoster
Jun 26, 2013

⚡POWER⚡

Mycroft Holmes posted:

Ok, I need to have a program exit if the user presses enter without inputting data. Is there a way to detect a blank field?

Sure, and here's a path to figuring something like that out:

code:
# See what happens when you put nothing in input
input()
Out[2]: ''

# Aight it looks like its an empty string, lets just make sure
type(input())
Out[3]: str

# Cool so I can exit on empty string if...
def get_input():
	x = input()
	if x == "":
		return "Empty string" #return whatever you want here...or if its part of a loop and you go to next iter, can say continue...sys.exit to kill program and so on

	

Mycroft Holmes
Mar 26, 2010

by Azathoth
can you put an if loop inside a try/except?

Jigsaw
Aug 14, 2008

Mycroft Holmes posted:

can you put an if loop inside a try/except?

You can put conditionals (if) and loops (for, while) inside try/except blocks.

Josh Lyman
May 24, 2009


On a related note, I have a fairly long script with a number of generic try/except blocks inside of loops, many of which are except: pass. In JupyterLab, when I click the stop button, it continues onto the next loop iteration. Shouldn't the stop button actually stop the notebook? Do I need to specify the type of error in my try/except blocks? Unfortunately, one of them is meant to catch an error when I call an API and it returns an empty list, and when I tried to put the name of that error in the except, it wasn’t recognized. Should I just use except Exception: in that case?

Josh Lyman fucked around with this message at 07:51 on Aug 25, 2022

QuarkJets
Sep 8, 2008

Josh Lyman posted:

On a related note, I have a fairly long script with a number of generic try/except blocks inside of loops, many of which are except: pass. In JupyterLab, when I click the stop button, it continues onto the next loop iteration. Shouldn't the stop button actually stop the notebook? Do I need to specify the type of error in my try/except blocks? Unfortunately, one of them is meant to catch an error when I call an API and it returns an empty list, and when I tried to put the name of that error in the except, it wasn’t recognized. Should I just use except Exception: in that case?

You should avoid broad Exception catching, usually.

Can you eliminate that try/except block by just checking the list length?

Josh Lyman
May 24, 2009


QuarkJets posted:

You should avoid broad Exception catching, usually.

Can you eliminate that try/except block by just checking the list length?
Sorry, I was thinking about a different version of the API that returns an empty list. When the error happens with the API call I’m interested in, it throws back an error saying I provided an invalid input (the thing I’m looking up isn’t there).

At the very least, this has gotten me to specify all my except statements (primarily IndexError).

Big Dick Cheney
Mar 30, 2007
Does anyone use Folium? I am trying to build a map with multiple layers:



Python code:
colormap = linear.YlGn_09.scale(0,100)

test=['a1', 'a2', 'a3', 'a4', 'a5']

for i in test:
	 fm.GeoJson(shapefile, name=str(i), style_function=(lambda feature: {
		'fillColor': colormap(feature.properties[i]),'color': 'black',
		'weight': 1,'dashArray': '5,5',; 'fillOpacity': 0.3})).add_child(fm.features.GeoJsonTooltip([i])).add_to(mp)

fm.LayerControl(collapsed=False).add_to(mp)

output_file='test.html'

mp.save(output_file)

webbrowser.open(output_file, new=2)

When I create the map, it will show all 5 layers. It will show the right number in the tooltip, the right name, but the colors of each map will be exactly the same. Each map will have the colors of the first map. What am I doing wrong?

Big Dick Cheney fucked around with this message at 21:08 on Aug 26, 2022

Zoracle Zed
Jul 10, 2001
no idea what Folium is, but I'd guess you're running into a classic lambda variable binding gotcha:

Python code:
fs = [lambda: i for i in range(3)]

for f in fs:
    print(f())
prints "2, 2, 2"

one kludge is to bind the variable to an argument default value:

Python code:
fs = [lambda i=i: i for i in range(3)]
which prints 0, 1, 2

of course there's other, much more readable solutions, like:

Python code:
def make_fn(i):
    return lambda: i

fs = [make_fn(i) for i in range(3)]

Falcon2001
Oct 10, 2004

Eat your hamburgers, Apollo.
Pillbug
Is there a simpler way to handle this interaction? I'm finding I'm doing this a reasonable amount.

Python code:
expected_var = object.possibly_null_attr if object.possibly_null_attr else default_value

Data Graham
Dec 28, 2009

📈📊🍪😋



Python code:
expected_var = object.possibly_null_attr or default_value
?

Armitag3
Mar 15, 2020

Forget it Jake, it's cybertown.


Falcon2001 posted:

Is there a simpler way to handle this interaction? I'm finding I'm doing this a reasonable amount.

Python code:
expected_var = object.possibly_null_attr if object.possibly_null_attr else default_value

You can use the built in getattr, similar syntax to dict.get("key", default).

Python code:
expected_var = getattr(object, "possibly_null_attr", default_value)
That is, if you mean null to mean the attribute doesn't exist. If it exists and is None, then it'll return that None, in which case you can:
  • Test for all falsy, at which point Data Graham's suggestion will work;
  • Test only for None which will require you to do what you are doing (recommend you add is None to your if)

Armitag3 fucked around with this message at 19:14 on Aug 31, 2022

Falcon2001
Oct 10, 2004

Eat your hamburgers, Apollo.
Pillbug

Data Graham posted:

Python code:
expected_var = object.possibly_null_attr or default_value
?

I didn't realize I could use Or like that, thanks!


Armitag3 posted:

You can use the built in getattr, similar syntax to dict.get("key", default).

Python code:
expected_var = getattr(object, "possibly_null_attr", default_value)
That is, if you mean null to mean the attribute doesn't exist. If it exists and is None, then it'll return that None, in which case you can:
  • Test for all falsy, at which point Data Graham's suggestion will work;
  • Test only for None which will require you to do what you are doing (recommend you add is None to your if)

Yeah, for purposes of this, I mean nullable to be None, not nonexistent. Falsiness is the check I'm trying for, to be clearer.

Falcon2001 fucked around with this message at 19:47 on Aug 31, 2022

12 rats tied together
Sep 7, 2006

If you find that you end up sprinkling
Python code:
object.possibly_null_attr or default_value
all throughout your codebase like a magic salve, that's also probably an indication that you're missing some kind of GuaranteedObject class with a safe api so that your dependents can interact with it without needing to guard against the null propagating throughout the rest of your business logic

Adbot
ADBOT LOVES YOU

Falcon2001
Oct 10, 2004

Eat your hamburgers, Apollo.
Pillbug

12 rats tied together posted:

If you find that you end up sprinkling
Python code:
object.possibly_null_attr or default_value
all throughout your codebase like a magic salve, that's also probably an indication that you're missing some kind of GuaranteedObject class with a safe api so that your dependents can interact with it without needing to guard against the null propagating throughout the rest of your business logic

Yeah, I know this is moving into antipattern territory. This portion is entirely internal, and none of these objects exit to my dependencies. In fact, a bit part of what I'm doing is trying to generate safe, fully generated objects to output by catching any places where things are null'd out during generation of those objects.

I think the proper way to do this would be with more exceptions and error handling instead of 'Optional[outputObject]', but this isn't something I'm shoving outside of this module at least.

Edit:

To expand on this, I guess I can just lay out what I'm doing and if I'm being an idiot, tell me.
Python code:
component_one = get_c1()
component_two = get_c2()
component_three = get_c3()

if not (c1 and c2 and c3):
	log("Cannot make this object!")
	return None

merged_output_object = build_merged_output_object(c1,c2,c3)

return merged_output_object
This is obviously pretty abstracted, but is a high level summary of it. Double edit: this doesn't even contain what I asked about before. I might be an idiot. Anyway, feel free to critique this too!

Falcon2001 fucked around with this message at 20:35 on Aug 31, 2022

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply