Re: [Tutor] (no subject)

2019-03-23 Thread Peter Otten
Matthew Herzog wrote:

> I have a Python3 script that reads the first eight characters of every
> filename in a directory in order to determine whether the file was created
> before or after 180 days ago based on each file's name. The file names all
> begin with MMDD or erased_MMDD_etc.xls. I can collect all these
> filenames already.
> I need to tell my script to ignore any filename that does not conform to
> the standard eight leading numerical characters, example: 20180922 or
> erased_20171207_1oIkZf.so.
> Here is my code.
> 
> if name.startswith('scrubbed_'):
> fileDate = datetime.strptime(name[9:17], DATEFMT).date()
> else:
> fileDate = datetime.strptime(name[0:8], DATEFMT).date()
> 
> I need logic to prevent the script from 'choking' on files that don't fit
> these patterns. The script needs to carry on with its work and forget
> about non-conformant filenames. Do I need to add code that causes an
> exception or just add an elif block?

Personally I would use a try...except clause because with that you can 
handle invalid dates like _etc.xls gracefully. So

ERASED = "erased_"


def strip_prefix(name):
if name.startswith(ERASED):
name = name[len(ERASED):]
return name


def extract_date(name):
datestr = strip_prefix(name)[:8]
return datetime.datetime.strptime(datestr, DATEFMT).date()


for name in ...:
try:
file_date = extract_date(name)
except ValueError:
pass
else:
print(file_date)


___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


[Tutor] properly propagate problems

2019-03-23 Thread ingo janssen
One thing I often struggle with is how to deal with exceptions, 
especially when I have a chain of functions that use each others output 
and/or long running processes. As the answer will probably be "it 
depends" take for example this program flow:


open a file and read into BytesIO buffer
get a FTP connection from pool
send buffer to plantuml.jar in memory FTP server
render file to image
get image from FTP server
push the image onto CherryPy bus
push (SSE) the image to web browser

def read_file(input_file):
try:
with open(input_file, 'rb') as f:
buffer = io.BytesIO(f.read())
except FileNotFoundError as e:
print(e)

return buffer

assume the file is not found, I cannot just kill the whole process. 
Catching the exception is one thing, but how to deal with it properly, I 
have to inform the client somehow what went wrong.


In this case I could push the error message into the returned buffer and 
just go from there and the image will show the message.


I could also bypass the whole process somehow and push the error message 
directly on the CherryPy bus.


What is wisdom, are there some general rules to follow in such cases

ingo
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


[Tutor] After virtualenv, how to use ide

2019-03-23 Thread anand warik
I had installed Python on Ubuntu 14.04 using Anaconda package long back
after failing to install independently for a long time. I was quietly using
it's packaged ide Spyder and had no troubles, in fact I love spider more
then atom. I recently learned how to setup a virtual environment though,
which recommends to never use system wide Python install. But even after
activating the virtual environment, I don't know how can i ask the spyder
ide to use the new Python directory. Can anyone help me change the setting
of Spyder so that it uses different versions?
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] After virtualenv, how to use ide

2019-03-23 Thread boB Stepp
On Sat, Mar 23, 2019 at 12:50 PM anand warik  wrote:
>
> I had installed Python on Ubuntu 14.04 using Anaconda package long back
> after failing to install independently for a long time. I was quietly using
> it's packaged ide Spyder and had no troubles, in fact I love spider more
> then atom. I recently learned how to setup a virtual environment though,
> which recommends to never use system wide Python install. But even after
> activating the virtual environment, I don't know how can i ask the spyder
> ide to use the new Python directory. Can anyone help me change the setting
> of Spyder so that it uses different versions?

A quick search yields this Stack Overflow thread with what appears to
be several useful links embedded:
https://stackoverflow.com/questions/30170468/how-to-run-spyder-in-virtual-environment
-- 
boB
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] (no subject)

2019-03-23 Thread Mats Wichmann
On 3/23/19 3:16 AM, Peter Otten wrote:

> Personally I would use a try...except clause because with that you can 
> handle invalid dates like _etc.xls gracefully. So
> 
> ERASED = "erased_"
> 
> 
> def strip_prefix(name):
> if name.startswith(ERASED):
> name = name[len(ERASED):]
> return name
> 
> 
> def extract_date(name):
> datestr = strip_prefix(name)[:8]
> return datetime.datetime.strptime(datestr, DATEFMT).date()
> 
> 
> for name in ...:
> try:
> file_date = extract_date(name)
> except ValueError:
> pass
> else:
> print(file_date)

I'd endorse this approach as well.. with a DATEFMT of "%Y%m%d", you will
get a ValueError for every date string that is not a valid date... which
you then just ignore (the "pass"), since that's what you wanted to do.

___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] properly propagate problems

2019-03-23 Thread Cameron Simpson

On 23Mar2019 11:04, ingo janssen  wrote:
One thing I often struggle with is how to deal with exceptions, 
especially when I have a chain of functions that use each others 
output and/or long running processes. As the answer will probably be 
"it depends"


Oh yes!

The core rule of thumb is "don't catch an exception which you don't know 
how to handle", but that is for truly unexpected errors not envisaged by 
the programmer. Then your programme aborts with a debugging stack trace.


Your situation below is more nuanced. Discussion below.


take for example this program flow:

open a file and read into BytesIO buffer
get a FTP connection from pool
send buffer to plantuml.jar in memory FTP server
render file to image
get image from FTP server
push the image onto CherryPy bus
push (SSE) the image to web browser

def read_file(input_file):
   try:
   with open(input_file, 'rb') as f:
   buffer = io.BytesIO(f.read())
   except FileNotFoundError as e:
   print(e)
   
   return buffer

assume the file is not found, I cannot just kill the whole process. 
Catching the exception is one thing, but how to deal with it properly, 
I have to inform the client somehow what went wrong.


Given a function like that, I would be inclined to do one of 2 things:

A) don't make a policy decision (catching the exception) this close to 
the failure, instead let the exception out and let the caller handle it:


   def read_file(input_file):
   with open(input_file, 'rb') as f:
   return io.BytesIO(f.read())

   filename = "foo"
   try:
   buffer = read_file(filename)
   except OSError as e:
   error("could not load %r: %s", filename, e)
   ... failure action, maybe return from the function ...
   ... proceed with buffer ...

This leaves the policy decision with the calling code, which may have a 
better idea about what is suitable. For example, you might pass some 
useful response to your web client here. The low level function 
read_file() doesn't know that it is part of a web service.


The handy thing about exceptions is that you can push that policy 
decision quite a long way out. Provided the outer layer where you decide 
to catch the exception knows that this involved accessing a file you can 
put that try/except quite a long way out and still produce a sensible 
looking error response.


Also, the further out the policy try/except lives, the simpler the inner 
functions can be because they don't need to handle failure - they can be 
written for success provided that failures raise exceptions, making them 
_much_ simpler and easier to maintain. And with far fewer policy 
decisions!


The flip side to this is that there is a limit to how far out in the 
call chain this try/except can sensibly happen: if you're far enough out 
that the catching code _doesn't_ know that there was a file read 
involved, the error message becomes more vague (although you still have 
the exception instance itself with the low level detail).


B) to return None on failure:

   def read_file(input_file):
   try:
   with open(input_file, 'rb') as f:
   return io.BytesIO(f.read())
   except OSError as e:
   error(
   "read_file(%r): could not read input file: %s", 
   input_file, e)

   return None

None is a useful sentinel value for failure. Note that sometimes you 
will want something else if None is meaningful return value in ordinary 
circumstances. Then your calling code can handle this without 
exceptions:


   buffer = read_file("foo")
   if buffer is None:
   ... return nice message to web client ...
   else:
   ... process the image ...

However, it does mean that this handling has to happen right at the call 
to read_file. That can be fine, but might be inconvenient.


Finally, some related points:

I find it useful to distinguish "mechanism" and "policy". In my ideal 
world a programme is at least 90% mechanism with a thin layer of policy 
outside it. Here "policy" is what might be termed "business logic" or 
"application logic" in some circumstances: what to do to achieve the 
high level goal. The high level is where you decide how to behave in 
various circumstances.


This has a few advantages: almost all low level code is mechanism: it 
has a well defined, usually simple, purpose. By having almost all 
failures raise an exception you can make the low level functions very 
simple: do A then B then C until success, where you return the result; 
raise exceptions when things go wrong (failure to open files, invalid 
input parameters, what have you). This produces what I tend to call 
"white list" code: code which only returns a result when all the 
required operations succeed.


This is option (A) above, and makes for very simple inner functions.

For option (B) "return None on failure", this is where we decide that 
specific failures are in fact valid execution paths, and None is a valid 
function return, indicating

[Tutor] How to avoid "UnboundLocalError: local variable 'goal_year' referenced before assignment"?

2019-03-23 Thread boB Stepp
I have just written a small program earlier today to allow the user
(me) to enter a date by which I wish to finish reading a book (=
massive programming-related book) and calculate how many pages I need
to read each day (starting today) in order to finish the book by the
target date.  Looking over my code I found that I was repeating a
fairly regular pattern in collecting the user input, so I thought I
would *improve* my working program by removing the duplicated code
into a single function.  So I came up with:

from datetime import date

def get_input(greeting_msg, identifier, input_prompt, date_value_err_ck,
err_msg, conditions):
""" ??? """

if greeting_msg:
print(greeting_msg)

while True:
try:
identifier = int(input(input_prompt))
if date_value_err_ck:
date(*date_value_err_ck)
except ValueError:
print(err_msg)
continue
for (condition, condition_msg) in conditions:
if condition:
print(condition_msg)
break
else:
return identifier

When I attempt to use this function with:

goal_year_params = {
'greeting_msg': "Please enter the date by which you wish to attain"
" your goal.\n",
'identifier': 'goal_year',
'input_prompt': "Enter year of your goal as an integer:  ",
'date_value_err_ck': (goal_year, 1, 1),
'err_msg': "That is not a valid year.  Please try again.",
'conditions': [
('goal_year < date.today().year',
"Have you invented a time machine?  If not, please enter a"
" year that makes more sense!"),
('goal_year >= date.today().year + 100',
"Have you discovered the secret to eternal life?  And how"
" long is this book anyway?  Please enter a year that"
" makes more sense.")]}
goal_year = get_input(**goal_year_params)

I get the following traceback:

Traceback (most recent call last):
  File "pages_per_day.py", line 250, in 
start_pgm()
  File "pages_per_day.py", line 246, in start_pgm
goal_date_obj, pages_read, total_pages_to_read = get_inputs()
  File "pages_per_day.py", line 63, in get_inputs
'date_value_err_ck': (goal_year, 1, 1),
UnboundLocalError: local variable 'goal_year' referenced before assignment

I understand this result, but cannot come up with a way to implement
my desired DRY strategy as I am stuck on how to get around this "local
variable ... referenced before assignment" issue.  On subsequent
passes "goal_year" will become "goal_month" and then "goal_day" as the
user needs to input all three of these numbers.  Is there a way to
accomplish my goal or am I attempting to be too clever?

-- 
boB
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] How to avoid "UnboundLocalError: local variable 'goal_year' referenced before assignment"?

2019-03-23 Thread Cameron Simpson

Discussion below your post here, since I feel I should quote it all:

On 23Mar2019 22:15, boB Stepp  wrote:

I have just written a small program earlier today to allow the user
(me) to enter a date by which I wish to finish reading a book (=
massive programming-related book) and calculate how many pages I need
to read each day (starting today) in order to finish the book by the
target date.  Looking over my code I found that I was repeating a
fairly regular pattern in collecting the user input, so I thought I
would *improve* my working program by removing the duplicated code
into a single function.  So I came up with:

from datetime import date

def get_input(greeting_msg, identifier, input_prompt, date_value_err_ck,
   err_msg, conditions):
   """ ??? """

   if greeting_msg:
   print(greeting_msg)

   while True:
   try:
   identifier = int(input(input_prompt))
   if date_value_err_ck:
   date(*date_value_err_ck)
   except ValueError:
   print(err_msg)
   continue
   for (condition, condition_msg) in conditions:
   if condition:
   print(condition_msg)
   break
   else:
   return identifier

When I attempt to use this function with:

goal_year_params = {
   'greeting_msg': "Please enter the date by which you wish to attain"
   " your goal.\n",
   'identifier': 'goal_year',
   'input_prompt': "Enter year of your goal as an integer:  ",
   'date_value_err_ck': (goal_year, 1, 1),
   'err_msg': "That is not a valid year.  Please try again.",
   'conditions': [
   ('goal_year < date.today().year',
   "Have you invented a time machine?  If not, please enter a"
   " year that makes more sense!"),
   ('goal_year >= date.today().year + 100',
   "Have you discovered the secret to eternal life?  And how"
   " long is this book anyway?  Please enter a year that"
   " makes more sense.")]}
goal_year = get_input(**goal_year_params)

I get the following traceback:

Traceback (most recent call last):
 File "pages_per_day.py", line 250, in 
   start_pgm()
 File "pages_per_day.py", line 246, in start_pgm
   goal_date_obj, pages_read, total_pages_to_read = get_inputs()
 File "pages_per_day.py", line 63, in get_inputs
   'date_value_err_ck': (goal_year, 1, 1),
UnboundLocalError: local variable 'goal_year' referenced before assignment

I understand this result, but cannot come up with a way to implement
my desired DRY strategy as I am stuck on how to get around this "local
variable ... referenced before assignment" issue.  On subsequent
passes "goal_year" will become "goal_month" and then "goal_day" as the
user needs to input all three of these numbers.  Is there a way to
accomplish my goal or am I attempting to be too clever?


You're not attemnpting to be too clever, but you are making some basic 
errors. For example, you can't just run a string like 'goal_year < 
date.today).year' and you shouldn't be trying i.e. do _not_ reach for 
the eval() function.


Let's look at your get_input() function. It basicly loops until you get 
a value you're happy with, and returns that value. Or it would if it 
were correct. Let's examine the main loop body:


   try:
   identifier = int(input(input_prompt))
   if date_value_err_ck:
   date(*date_value_err_ck)
   except ValueError:
   print(err_msg)
   continue
   for (condition, condition_msg) in conditions:
   if condition:
   print(condition_msg)
   break
   else:
   return identifier

To start with, you have confusion in the code bwteen the name you're 
intending to use for the input value (the "identifier" parameter) and 
the value you're reading from the user. You go:


   identifier = int(input(input_prompt))

That immediatey destroys the name you passed in as a parameter. Instead, 
use a distinct variable for the input value. Let's be imaginitive and 
call it "value":


   value = int(input(input_prompt))

and at the bottom of the get_input() function you should:

   return value

Then you try to create a date from that value (though you don't save it 
anywhere). I presume you want to use the datetime.date() constructor.  
So:


   # at the start of your programme
   import datetime

then in the function:

   date = datetime.date(*date_value_err_ck)

I think your plan is that datetime.date() will also raise a ValueError 
for a bad year number in "value". So you want, in fact, to call:


   date = datetime.date(goal_year, 1, 1)

And the construction of that will be different on your subsequent calls 
where you have goal_year and are inputting goal_month, and so on.


Your date_value_err_ck comes from your dict at the bottom of the 
programme. However, it is in the natural of things that you're computing 
the content of that dict _before_ you call get_input().


Let 

Re: [Tutor] How to avoid "UnboundLocalError: local variable 'goal_year' referenced before assignment"?

2019-03-23 Thread boB Stepp
Ah!  After another long break from coding, I resume a pattern of one
step forward, who knows how many steps back, as I forget what I once
remembered and understood.

It is the wee morning hours where I am at, so I don't think I will
make it through everything, but I will make a start...

On Sun, Mar 24, 2019 at 12:22 AM Cameron Simpson  wrote:
>
> Discussion below your post here, since I feel I should quote it all:
>
> On 23Mar2019 22:15, boB Stepp  wrote:

> >Traceback (most recent call last):
> >  File "pages_per_day.py", line 250, in 
> >start_pgm()
> >  File "pages_per_day.py", line 246, in start_pgm
> >goal_date_obj, pages_read, total_pages_to_read = get_inputs()
> >  File "pages_per_day.py", line 63, in get_inputs
> >'date_value_err_ck': (goal_year, 1, 1),
> >UnboundLocalError: local variable 'goal_year' referenced before assignment
> >
> >I understand this result, but cannot come up with a way to implement
> >my desired DRY strategy as I am stuck on how to get around this "local
> >variable ... referenced before assignment" issue.  On subsequent
> >passes "goal_year" will become "goal_month" and then "goal_day" as the
> >user needs to input all three of these numbers.  Is there a way to
> >accomplish my goal or am I attempting to be too clever?
>
> You're not attemnpting to be too clever, but you are making some basic
> errors. For example, you can't just run a string like 'goal_year <
> date.today).year' and you shouldn't be trying i.e. do _not_ reach for
> the eval() function.

Too late!  About the time your message hit my inbox, I just finished a
working replacement after pulling the the eval() trigger.  I found a
non-eval() workaround for dealing with date(*date_value_err_ck), but
could not come up with anything better than using
eval(condition.format(user_input)) to replace the "if condition:",
where the embedded "{0}" in "condition" from the calling code is being
used to pass in the actual user input value.  I imagine a clever user
(other than myself) could now wreak all sorts of havoc!  I totally got
rid of "identifier" as an argument.

> Let's look at your get_input() function. It basicly loops until you get
> a value you're happy with, and returns that value. Or it would if it
> were correct. Let's examine the main loop body:
>
> try:
> identifier = int(input(input_prompt))
> if date_value_err_ck:
> date(*date_value_err_ck)
> except ValueError:
> print(err_msg)
> continue
> for (condition, condition_msg) in conditions:
> if condition:
> print(condition_msg)
> break
> else:
> return identifier
>
> To start with, you have confusion in the code bwteen the name you're
> intending to use for the input value (the "identifier" parameter) and
> the value you're reading from the user. You go:

It wasn't really confusion on my part.  What I *wanted* to do was to
substitute a more specific identifier from the calling code for the
generic "identifier" in the get_input() function, and use it both for
the actual user input and to fill in that value wherever I need it.
But I could not find a way to accomplish this and just left my
question at the last error state.  But I guess this is not a doable
thing.

> identifier = int(input(input_prompt))
>
> That immediatey destroys the name you passed in as a parameter. Instead,
> use a distinct variable for the input value. Let's be imaginitive and
> call it "value":

Or "user_input", which I finally wound up with.

> Then you try to create a date from that value (though you don't save it
> anywhere). I presume you want to use the datetime.date() constructor.
> So:
>
> # at the start of your programme
> import datetime
>
> then in the function:
>
> date = datetime.date(*date_value_err_ck)
>
> I think your plan is that datetime.date() will also raise a ValueError
> for a bad year number in "value". So you want, in fact, to call:

That is, in fact, the plan.  Since I only want to check for a bad
year, then a bad month, and finally, a bad day of the month, I saw no
point in binding a name to the date object.  In the part of the code I
did not include, I create a date object from date(goal_year,
goal_month, goal_day) to use in the rest of the program.

> Let me introduce you to the lambda.

Or, in my case, "reintroduce" me to the lambda.  It has been probably
at least a couple of years since I had need of this and I had totally
forgotten about it!  There are those backward steps again!

I guess I will have to head for bed now and look more closely at the
rest later today.  But I can see that this totally eliminates any need
for eval() -- which I did not want to use anyway -- and solves what I
was struggling so much with.

As for closures, I will have to read that portion *most* carefully
later.  I know I have asked some questions in the past where these
were brought up as answers, but this did not stick in my brain at all.

Thanks, Cameron,