Hello Richard,
In addition to the answers you have received from Alex and Alan, I'll add a bit longer of an answer mostly around how to separate the command-line invocation / argument handling part of your program from the pure-Python logic. While I, also, cannot help terribly with the direct question about your IDLE environment, I hope this explanation is generally helpful and useful. >I am working on a python script that will be provided arguments >when run from the system command line. Is there any place in IDLE >to provide equivalent arguments for testing while developing in >IDLE? I can't really help so much with the IDLE part of the question, but I do have a solution that has worked very well for me for testing programs that are usually invoked by command-line. My answer will focus on that angle rather than the IDLE angle. >Is there any way to define the working directory for the program, >or will it always be the directory the script is in (it will be >typically run using the PATH, so not the same directory as the >script)? Defining working directory. --------------------------- You cannot control the initial working directory when your program has been executed. It was executed by another process (or human, or alien from the Ophiucus Cluster) so your working directory is whatever was in the parent parent process just before forking/execution of your program. However, you are free to chdir() once your program is running: >>> os.getcwd() '/home/mabrown' >>> os.chdir('/home/mabrown/tmp/') >>> os.getcwd() '/home/mabrown/tmp' One bit of care that is warranted when you are calling os.chdir() (besides the usual error-checking, 'Can the user running this program actually chdir() to that directory?)' is to think about whether you want your program to use relative paths or absolute paths. It's worth thinking about early because if you don't, you can end up with a mess of relative and absolute paths. That way madness, bugs and possible data loss lie. What is my install directory? ----------------------------- If you need to know what directory your Python program is in, you can use os.path.dirname(os.path.abspath(__file__)). Sometimes, there are reasons to find your data (or other code?) in the same directory as (or nearby) your Python program. Think carefully about this and consider how that might work with your Python packaging solution, if you are going down that path. In general, I have tried to use command-line options to find data directories or other files needed by a Python program, as that is more intelligible to users (and to me when I come back to the code a year later) than the automatic discovery of files that just happen to be in the same directory as my Python program. Random note on $PATH and full pathnames --------------------------------------- Have you ever been trying to diagnose a problem with a script and you realized about four months two late (because that 20 minute debugging session where you think you are going insane feels like four months by the time you are done), and you realized at the end that the problem was that you thought you were running a copy of script A in directory A, but you were actually running older copy B in directory B. This meant that none of your changes were being reflected in the output and you couldn't figure it out. Well, I'll tell you, I have never, ever had that experience! Nope. Not even once. Yeah. Anyway, I have found that logging os.path.abspath(__file__) to the system log (or to STDERR or elsewhere) what the full path is to the file that is executing. This is useful to avoid the "I think I'm running my dev copy, but am actually running something else." sort of problem. >If not, is there an easy way to detect that I am running in IDLE so >I can fake the command line arguments when testing? I'll give you an example (see below) of how I write the Python to call most of my programs that are invoked directly from the command-line. The basic idea (which I think I picked up here) is to turn anything that you want to be able to test in your program into Python variables almost as soon as the program begins. That way, things like STDIN, STDOUT and the arguments become simply file objects and lists of strings. With file objects and lists of strings you can write testing functions to see how your program behaves when invoked a certain way. So, this is fairly unexciting code: def cli(me, fin, fout, argv): config, args = collectconfiguration(me, argv) rc = process_something(fin, fout, config) if rc == 0: return os.EX_OK return 1 if __name__ == '__main__': me = os.path.basename(sys.argv[0]) sys.exit(cli(me, sys.stdin, sys.stdout, sys.argv[1:])) You will see that I did not add os.environ to the argument list to the function cli(), but if my program(s) did anything with environment variables, I would add that here. You will also see that I don't mess with sys.stderr. In general, I like to stay away from redirecting that unless there's a strong need. Benefits: * you can call cli() in your testing programs with different lists of command-line invocations to see how your program behaves * you can see how -Martin Note: You will see in this example of collectconfiguration() (possibly not the best name for this function) that I make reference to sys.stdin and sys.stdout. They are also arguments to cli(). I think that is OK because I use this sort of calling structure many places and I want to keep the signature for the cli() function the same everywhere. In short, some programs may not actually have the '--input' / '--output' command-line options, in which case cli() has the variables fin and fout to hold those. def collectconfiguration(me, argv): ap = argparse.ArgumentParser(prog=me) ap.add_argument('--input', default=sys.stdin, type=argparse.FileType('r'), help='where to read input data [%(default)s]') ap.add_argument('--output', default=sys.stdout, type=argparse.FileType('w'), help='where to write output data [%(default)s]') ap.add_argument('--apiurl', default='http://localhost:8080', help='specify alternate API URL [%(default)s]') ap.add_argument('--delimiter', default='\t', type=str, help='set delimiter [%(default)s]') ap.add_argument('--loglevel', default=logging.ERROR, type=arg_isloglevel, help='set loglevel, e.g. INFO, ERROR [%(default)s]') ap.add_argument('--version', action='version', version=__version__) # -- and handle the CLI # config, args = ap.parse_known_args(argv) logger.setLevel(config.loglevel) logger.debug("Received the following configuration:") for param, value in sorted(vars(config).items()): logger.debug(" %s = %r", param, value) logger.debug(" args: %r", args) if args: ap.print_help() sys.exit("\nExtra arguments received: %r" % (args,)) return config, args def arg_isloglevel(l, defaultlevel=logging.ERROR): try: level = int(l) return level except ValueError: pass level = getattr(logging, l.upper(), None) if not level: level = defaultlevel return level -- Martin A. Brown http://linux-ip.net/ _______________________________________________ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor