| | 36 | |
|---|
| | 37 | |
|---|
| | 38 | OPTIONS_runex=[ |
|---|
| | 39 | ('--log-level', dict(default='WARNING', metavar='LEVEL', help= |
|---|
| | 40 | """[default:%default] set the logging level, any string which names |
|---|
| | 41 | a log level which is defined by the logging package is allowed. |
|---|
| | 42 | For example any of CRITICAL, WARNING, INFO and DEBUG (in |
|---|
| | 43 | increasing order of verbosity)""")), |
|---|
| | 44 | ('-q', dict(default=False, action='store_true', metavar='QUIET', help= |
|---|
| | 45 | """Suppress all warnings about missing paths etc. Useful when you are |
|---|
| | 46 | using speculative paths and are using -p or -P to print the discoverd |
|---|
| | 47 | path.""")), |
|---|
| | 48 | |
|---|
| | 49 | ('-p', dict(default=False, action='store_true', metavar='PRINTPATH', help= |
|---|
| | 50 | "Print the discovered path")), |
|---|
| | 51 | |
|---|
| | 52 | ('-P', dict(default=False, action='store_true', metavar='PRINTPATH', help= |
|---|
| | 53 | "Print the discovered path in a PYTHONPATH compatible format")), |
|---|
| | 54 | |
|---|
| | 55 | |
|---|
| | 56 | ('-n', dict(default=False, action='store_true', metavar='NORUN', help= |
|---|
| | 57 | """NORUN. Don't run any of the modules implied by module file references in |
|---|
| | 58 | the discovery path.""")), |
|---|
| | 59 | |
|---|
| | 60 | ('-C', dict(default=None, type='string', metavar='SCRIPT', help= |
|---|
| | 61 | """Identify a *python* SCRIPT to execute. The script need not have file |
|---|
| | 62 | extension but it must contain leagal python code. This option trumps -m. This |
|---|
| | 63 | option should only be necessary when the launcher for the python program you |
|---|
| | 64 | wish to run contains significant functionality. No additions are made to the |
|---|
| | 65 | discovery path or sys.path as a result of using this option. If the target |
|---|
| | 66 | script imports a related package you will need to include additional non option |
|---|
| | 67 | arguments to discover its path.""")), |
|---|
| | 68 | |
|---|
| | 69 | ('-m', dict(default='', metavar='MODULE', help= |
|---|
| | 70 | "Explicitly select a module to run. (trumped by -S)")), |
|---|
| | 71 | |
|---|
| | 72 | ('-d', dict(default=False, action='store_true', metavar='DEBUG', help= |
|---|
| | 73 | """DEBUG session. Use `pdb.runeval` on the module code in order to enter |
|---|
| | 74 | an interactive debug session at the first python statement of the |
|---|
| | 75 | target module""")), |
|---|
| | 76 | |
|---|
| | 77 | ('-D', dict(default=False, action='store_true', metavar='DEBUG', help= |
|---|
| | 78 | """POSTMORTEM debugging. If the target raises an exception, start a |
|---|
| | 79 | postmortem pdb debugging session.""")), |
|---|
| | 80 | |
|---|
| | 81 | ('-i', dict(default=False, action='store_true', metavar='INTERACTIVE', help= |
|---|
| | 82 | "INTERACTIVE session with prepared sys.argv and sys.path.")), |
|---|
| | 83 | |
|---|
| | 84 | ('-c', dict(default=False, metavar='STATEMENT', help= |
|---|
| | 85 | """Update sys.argv and sys.path then execute the statement in a new, clean, |
|---|
| | 86 | module context.""")), |
|---|
| | 87 | |
|---|
| | 88 | ('-x', dict(default="", metavar='EXCLUDE', help= |
|---|
| | 89 | """Exclude one or more directories, separated by "%s", from the discovery |
|---|
| | 90 | path.""" % os.pathsep)), |
|---|
| | 91 | ('-X', dict(default=[], metavar='PRUNE', action="append", type="string", help= |
|---|
| | 92 | """Prune all paths which contain this value from the set of paths which *were* |
|---|
| | 93 | discovered. Specify multiple -X options if you wish too prune based on |
|---|
| | 94 | more than one string.""")) |
|---|
| | 95 | |
|---|
| | 96 | ] |
|---|
| 676 | | def int_log_level(level): |
|---|
| 677 | | """Coerce a log level to an integer. |
|---|
| 678 | | |
|---|
| 679 | | In a manner cognizant of run time configured level names.""" |
|---|
| 680 | | |
|---|
| 681 | | try: |
|---|
| 682 | | return int(level) |
|---|
| 683 | | except ValueError: |
|---|
| 684 | | try: |
|---|
| 685 | | return getattr(logging, level) |
|---|
| 686 | | except AttributeError: |
|---|
| 687 | | level = logging.getLevelName(level) |
|---|
| 688 | | level = logging.getLevelName(level) |
|---|
| 689 | | assert isinstance(level, int) |
|---|
| 690 | | return level |
|---|
| 691 | | |
|---|
| 692 | | |
|---|
| 693 | | def runex(argv=None): |
|---|
| 694 | | """Provides Extened `pyrun` features on the command line.""" |
|---|
| 695 | | |
|---|
| 696 | | # Process the command line arguments. |
|---|
| 697 | | import optparse |
|---|
| 698 | | parser = optparse.OptionParser(usage=USAGE_runex) |
|---|
| 699 | | default_opts = {} |
|---|
| 700 | | for shrt, kw in OPTIONS_runex: |
|---|
| 701 | | default_opts[shrt[1]] = kw['default'] |
|---|
| 702 | | parser.add_option(shrt, **kw) |
|---|
| 703 | | |
|---|
| 704 | | parser.disable_interspersed_args() |
|---|
| 705 | | |
|---|
| 706 | | opts, args = parser.parse_args() |
|---|
| 707 | | argv = args[:] |
|---|
| | 756 | |
|---|
| | 757 | def pyrun_opts(**kw): |
|---|
| | 758 | """Convert keyword arguments to opts instance for `pyrun' |
|---|
| | 759 | |
|---|
| | 760 | raises TypeError if any keyword is present which is not a long or short |
|---|
| | 761 | option defined for the command line tool. Default values are filled in |
|---|
| | 762 | based on those used for the command line tool. |
|---|
| | 763 | |
|---|
| | 764 | |
|---|
| | 765 | :Returns: |
|---|
| | 766 | opts |
|---|
| | 767 | An instance, synthesized using `type', whose attributes correspond |
|---|
| | 768 | to the provided keywords. The default value for any pyrun option |
|---|
| | 769 | which is not provided via `kw' is included. |
|---|
| | 770 | defaultset |
|---|
| | 771 | A list of those options (co-erced to legal attribute names) which |
|---|
| | 772 | took on default values. |
|---|
| | 773 | notset |
|---|
| | 774 | A list of those options (co-erced to legal attribute names) which |
|---|
| | 775 | where not provided and for which no default exists. |
|---|
| | 776 | |
|---|
| | 777 | """ |
|---|
| | 778 | |
|---|
| | 779 | # pyrun options are all *either* short options or long options, none |
|---|
| | 780 | # have both sort and long forms. |
|---|
| | 781 | optdefs = dict(OPTIONS_runex) |
|---|
| | 782 | present = dict() |
|---|
| | 783 | def k_to_opt(k): |
|---|
| | 784 | if len(k) == 1: |
|---|
| | 785 | return '-' + k |
|---|
| | 786 | else: |
|---|
| | 787 | return '--' + k.replace('_', '-') |
|---|
| | 788 | def opt_to_k(opt): |
|---|
| | 789 | if opt.startswith('--'): |
|---|
| | 790 | return opt[2:].replace('-', '_') |
|---|
| | 791 | else: |
|---|
| | 792 | return opt[1] |
|---|
| | 793 | |
|---|
| | 794 | for k, v in kw.iteritems(): |
|---|
| | 795 | opt = k_to_opt(k) |
|---|
| | 796 | if opt not in optdefs: |
|---|
| | 797 | raise TypeError( |
|---|
| | 798 | 'Option "%s" not supported.' % opt) |
|---|
| | 799 | else: |
|---|
| | 800 | print 'present', opt |
|---|
| | 801 | present[k] = v |
|---|
| | 802 | continue |
|---|
| | 803 | |
|---|
| | 804 | opts = present.copy() |
|---|
| | 805 | defaultset = [] |
|---|
| | 806 | notset = [] |
|---|
| | 807 | for absent in set(optdefs.keys()) - set(present.keys()): |
|---|
| | 808 | kabsent = opt_to_k(absent) |
|---|
| | 809 | if 'default' in optdefs[absent]: |
|---|
| | 810 | opts[kabsent] = optdefs[absent]['default'] |
|---|
| | 811 | defaultset.append(kabsent) |
|---|
| | 812 | else: |
|---|
| | 813 | notset.append(kabsent) |
|---|
| | 814 | |
|---|
| | 815 | return type('PyRunOptions', (), opts)(), defaultset, notset |
|---|
| | 816 | |
|---|
| | 817 | |
|---|
| | 818 | def pyrun(opts, discovery_args): |
|---|
| | 819 | |
|---|
| | 820 | default_opts = get_default_opts() |
|---|
| | 821 | |
|---|
| | 822 | argv = discovery_args[:] |
|---|
| | 981 | |
|---|
| | 982 | |
|---|
| | 983 | def int_log_level(level): |
|---|
| | 984 | """Coerce a log level to an integer. |
|---|
| | 985 | |
|---|
| | 986 | In a manner cognizant of run time configured level names.""" |
|---|
| | 987 | |
|---|
| | 988 | try: |
|---|
| | 989 | return int(level) |
|---|
| | 990 | except ValueError: |
|---|
| | 991 | try: |
|---|
| | 992 | return getattr(logging, level) |
|---|
| | 993 | except AttributeError: |
|---|
| | 994 | level = logging.getLevelName(level) |
|---|
| | 995 | level = logging.getLevelName(level) |
|---|
| | 996 | assert isinstance(level, int) |
|---|
| | 997 | return level |
|---|
| | 998 | |
|---|
| | 999 | |
|---|
| | 1000 | def _pyrun_cl_parse_log_init(argv=None): |
|---|
| | 1001 | """Command line argument parsing and logging intialisation. |
|---|
| | 1002 | |
|---|
| | 1003 | :Returns: |
|---|
| | 1004 | opts |
|---|
| | 1005 | optparse.OptionValues instance containing the pyrun options |
|---|
| | 1006 | which preceded the discover path |
|---|
| | 1007 | args |
|---|
| | 1008 | In order, pyrun discovery path, pyrun terminating options, and the |
|---|
| | 1009 | options and arguments for the inferior programs. |
|---|
| | 1010 | |
|---|
| | 1011 | """ |
|---|
| | 1012 | # Process the command line arguments. |
|---|
| | 1013 | import optparse |
|---|
| | 1014 | parser = optparse.OptionParser(usage=__doc__) |
|---|
| | 1015 | for shrt, kw in OPTIONS_runex: |
|---|
| | 1016 | parser.add_option(shrt, **kw) |
|---|
| | 1017 | |
|---|
| | 1018 | parser.disable_interspersed_args() |
|---|
| | 1019 | |
|---|
| | 1020 | opts, args = parser.parse_args(argv or sys.argv[1:]) |
|---|
| | 1021 | |
|---|
| | 1022 | logging.basicConfig( |
|---|
| | 1023 | level=int_log_level(getattr(opts, 'log_level', 'WARNING')), |
|---|
| | 1024 | format='%(message)s' |
|---|
| | 1025 | ) |
|---|
| | 1026 | return opts, args |
|---|
| | 1027 | |
|---|
| | 1028 | |
|---|
| | 1029 | def get_default_opts(): |
|---|
| | 1030 | """Return a dictionary containing the default pyrun option values.""" |
|---|
| | 1031 | default_opts = {} |
|---|
| | 1032 | for shrt, kw in OPTIONS_runex: |
|---|
| | 1033 | default_opts[shrt[1]] = kw['default'] |
|---|
| | 1034 | return default_opts |
|---|
| | 1035 | |
|---|
| | 1036 | |
|---|
| | 1037 | def runex(argv=None): |
|---|
| | 1038 | """Provides Extened `pyrun` features on the command line.""" |
|---|
| | 1039 | |
|---|
| | 1040 | # Process the command line arguments which appear *before* the discovery |
|---|
| | 1041 | # path. |
|---|
| | 1042 | opts, discovery_args = _pyrun_cl_parse_log_init(argv) |
|---|
| | 1043 | return pyrun(opts, discovery_args) |
|---|
| 907 | | USAGE_runex="""\ |
|---|
| 908 | | %prog [-nidDpP] [BASEPATH(s)][-m mod.name | '--' | '-' ] [TARGET-OPTIONS] |
|---|
| 909 | | |
|---|
| 910 | | Discover python packages and modules under BASEPATH(s). Run the first module |
|---|
| 911 | | file named in `BASEPATH(s)` *OR* explicitly nominated using the `-m` option. |
|---|
| 912 | | BASEPATH can reference python .pth files, in which case the paths in that file |
|---|
| 913 | | are inserted. |
|---|
| 914 | | |
|---|
| 915 | | Note that the 'import ' lines are not supported in .pth files, that the paths |
|---|
| 916 | | are interpreted as relative to the directory containing the .pth file and that |
|---|
| 917 | | environment variables and '~' are fully expanded. |
|---|
| 918 | | |
|---|
| 919 | | In most cases the solo '--' or '-' is not required. It tends to be useful when |
|---|
| 920 | | you implicitly select the module to run AND you want to pass a non option |
|---|
| 921 | | argument as the first value in the command line for that module. It can also be |
|---|
| 922 | | necessary when the target module has short options, without long-name |
|---|
| 923 | | alternatives, which collide with those defined for pyrun. |
|---|
| 924 | | |
|---|
| 925 | | NOTE: Any option that is marked [NYI] is Not Yet Implemented.""" |
|---|
| 926 | | |
|---|
| 927 | | OPTIONS_runex=[ |
|---|
| 928 | | ('--log-level', dict(default='WARNING', metavar='LEVEL', help= |
|---|
| 929 | | """[default:%default] set the logging level, any string which names |
|---|
| 930 | | a log level which is defined by the logging package is allowed. |
|---|
| 931 | | For example any of CRITICAL, WARNING, INFO and DEBUG (in |
|---|
| 932 | | increasing order of verbosity)""")), |
|---|
| 933 | | ('-q', dict(default=False, action='store_true', metavar='QUIET', help= |
|---|
| 934 | | """Suppress all warnings about missing paths etc. Useful when you are |
|---|
| 935 | | using speculative paths and are using -p or -P to print the discoverd |
|---|
| 936 | | path.""")), |
|---|
| 937 | | |
|---|
| 938 | | ('-p', dict(default=False, action='store_true', metavar='PRINTPATH', help= |
|---|
| 939 | | "Print the discovered path")), |
|---|
| 940 | | |
|---|
| 941 | | ('-P', dict(default=False, action='store_true', metavar='PRINTPATH', help= |
|---|
| 942 | | "Print the discovered path in a PYTHONPATH compatible format")), |
|---|
| 943 | | |
|---|
| 944 | | |
|---|
| 945 | | ('-n', dict(default=False, action='store_true', metavar='NORUN', help= |
|---|
| 946 | | """NORUN. Don't run any of the modules implied by module file references in |
|---|
| 947 | | the discovery path.""")), |
|---|
| 948 | | |
|---|
| 949 | | ('-C', dict(default=None, type='string', metavar='SCRIPT', help= |
|---|
| 950 | | """Identify a *python* SCRIPT to execute. The script need not have file |
|---|
| 951 | | extension but it must contain leagal python code. This option trumps -m. This |
|---|
| 952 | | option should only be necessary when the launcher for the python program you |
|---|
| 953 | | wish to run contains significant functionality. No additions are made to the |
|---|
| 954 | | discovery path or sys.path as a result of using this option. If the target |
|---|
| 955 | | script imports a related package you will need to include additional non option |
|---|
| 956 | | arguments to discover its path.""")), |
|---|
| 957 | | |
|---|
| 958 | | ('-m', dict(default='', metavar='MODULE', help= |
|---|
| 959 | | "Explicitly select a module to run. (trumped by -S)")), |
|---|
| 960 | | |
|---|
| 961 | | ('-d', dict(default=False, action='store_true', metavar='DEBUG', help= |
|---|
| 962 | | """DEBUG session. Use `pdb.runeval` on the module code in order to enter |
|---|
| 963 | | an interactive debug session at the first python statement of the |
|---|
| 964 | | target module""")), |
|---|
| 965 | | |
|---|
| 966 | | ('-D', dict(default=False, action='store_true', metavar='DEBUG', help= |
|---|
| 967 | | """POSTMORTEM debugging. If the target raises an exception, start a |
|---|
| 968 | | postmortem pdb debugging session.""")), |
|---|
| 969 | | |
|---|
| 970 | | ('-i', dict(default=False, action='store_true', metavar='INTERACTIVE', help= |
|---|
| 971 | | "INTERACTIVE session with prepared sys.argv and sys.path.")), |
|---|
| 972 | | |
|---|
| 973 | | ('-c', dict(default=False, metavar='STATEMENT', help= |
|---|
| 974 | | """Update sys.argv and sys.path then execute the statement in a new, clean, |
|---|
| 975 | | module context.""")), |
|---|
| 976 | | |
|---|
| 977 | | ('-x', dict(default="", metavar='EXCLUDE', help= |
|---|
| 978 | | """Exclude one or more directories, separated by "%s", from the discovery |
|---|
| 979 | | path.""" % os.pathsep)), |
|---|
| 980 | | ('-X', dict(default=[], metavar='PRUNE', action="append", type="string", help= |
|---|
| 981 | | """Prune all paths which contain this value from the set of paths which *were* |
|---|
| 982 | | discovered. Specify multiple -X options if you wish too prune based on |
|---|
| 983 | | more than one string.""")) |
|---|
| 984 | | |
|---|
| 985 | | ] |
|---|