Post by Cameron SimpsonHowever, you'r eusing input(), which unconditionally uses stdin and
[... temporarily replace stdin and stdout with test data ...]
Yes indeed, and thank you for your input.
[...]
test_data = 'test_src.txt'
"""
"""
Minor remark: I would write "if src is not None:". In principle the empty
string is also "falsey" like None, making your plain "if src:" slightly
unreliable. Be precise!
Post by Cameron Simpsonostdin = sys.stdin
ostdout = sys.stdout
src = open(source, 'r')
sys.stdin = src
out = open('/dev/null', 'w') # Dump the prompts.
sys.stdout = out
ret = collect()
src.close()
out.close()
sys.stdin = ostdin
sys.stdout = ostdout
return ret
ret = {}
ret['first'] = input("Enter your first name: ")
ret['last'] = input("Enter your last name: ")
ret['phone'] = input("Your mobile phone #: ")
return ret
That looks like what I had in mind.
If you expect to do this with several functions you could write a context
manager to push new values for stdin and stdout, call a function and restore.
The "contextlib" stdlib module provides a convenient way to write trivial
context managers using the "@contextmanager" decorator, which wraps a generator
function which does the before/after steps. Have a read. I'd be inclined to
write something like this (untested):
import sys
from contextlib import contextmanager
@contextmanager
def temp_stdinout(src, dst):
ostdin = sys.stdin
ostdout = sys.stdout
sys.stdin = src
sys.stdout = dst
yield None
sys.stdin = ostdin
sys.stdout = ostdout
and then in your test code:
with open(source) as src:
with open('/dev/null', 'w') as dst:
with temp_stdinout(src, dst):
ret = collect()
This has several benefits. Primarily, a context manager's "after" code _always_
runs, even if an exception is raise in the inner section. This means that the
files are always closed, and the old stdin and stdout always restored. This is
very useful.
You'll notice also that an open file is a context manager which can be used
with the "with" statement: it always closes the file.
You also asked (off list) what I meant by parameterisation. I mean that some of
your difficult stems from "collect_data" unconditionally using stdin and
stdout< and that you can make it more flexible by supplying the input and
output as paramaters to the function. Example (not finished):
def collect_data(src, dst):
ret = {}
ret['first'] = input("Enter your first name: ")
ret['last'] = input("Enter your last name: ")
ret['phone'] = input("Your mobile phone #: ")
return ret
Now, the input() builtin always uses stdin and stdout, but it is not hard to
write your own:
def prompt_for(prompt, src, dst):
dst.write(prompt)
dst.flush()
return src.readline()
and use it in collect_data:
def collect_data(src, dst):
ret = {}
ret['first'] = prompt_for("Enter your first name: ", src, dst)
ret['last'] = prompt_for("Enter your last name: ", src, dst)
ret['phone'] = prompt_for("Your mobile phone #: ", src, dst)
return ret
You can also make src and dst optional, falling back to stdin and stdout:
def collect_data(src=None, dst=None):
if src is None:
src = sys.stdin
if dst is None:
dst = sys.stdout
ret = {}
ret['first'] = prompt_for("Enter your first name: ", src, dst)
ret['last'] = prompt_for("Enter your last name: ", src, dst)
ret['phone'] = prompt_for("Your mobile phone #: ", src, dst)
return ret
Personally I would resist that in this case because the last thing you really
want in a function is for it to silently latch onto your input/output if you
forget to call it with all its arguments/parameters. Default are better for
things that do not have side effects.
Post by Cameron Simpsonprint(collect_data()) # < check that user input works
# then check that can test can be automated >
print(data_collection_wrapper(collect_data,
src=test_data))
main()
Perhaps data_collection_wrapper could be made into a decorator (about
which I am still pretty naive.)
It'll take more studying on my part before I'll be able to implement
Ben's suggestion.
Alex
ps I was tempted to change the "Subject:" to remove 'cli' and replace
it with 'interactive' but remember many admonitions to not do that so
have left it as is.
Best to leave these things as they are unless the topic totally changes. Then I
tend to adjust the Subject: to be:
Subject: very different topic (was: old topic)
presuming that it is still the same discussion.
Cheers,
Cameron Simpson <***@zip.com.au>
_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor