Coverage for cclib/scripts/ccget.py : 64%
Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#
4# Copyright (c) 2017, the cclib development team
5#
6# This file is part of cclib (http://cclib.github.io) and is distributed under
7# the terms of the BSD 3-Clause License.
9"""Script for loading data from computational chemistry files."""
12import glob
13import logging
14import os.path
15import difflib
16from functools import partial
17from pprint import pprint
19# This is needed for testing purposes only.
20import sys
22import numpy
24from cclib.parser import ccData
25from cclib.io import ccread, URL_PATTERN
28# Set up options for pretty-printing output.
29pprint = partial(pprint, width=120, compact=True)
30numpy.set_printoptions(linewidth=120)
33def ccget():
34 """Parse files with cclib based on command line arguments."""
36 import argparse
38 parser = argparse.ArgumentParser()
40 parser.add_argument(
41 "attribute_or_compchemlogfile", nargs="+",
42 help="one or more attributes to be parsed from one ore more logfiles",
43 )
45 group = parser.add_mutually_exclusive_group()
47 group.add_argument(
48 "--list", "-l",
49 action="store_true",
50 help="print a list of attributes available in each file",
51 )
52 group.add_argument(
53 "--json", "-j",
54 action="store_true",
55 help="the given logfile is in CJSON format",
56 )
57 group.add_argument(
58 "--multi", "-m",
59 action="store_true",
60 help="parse multiple input files as one input stream",
61 )
63 parser.add_argument(
64 "--verbose", "-v",
65 action="store_true",
66 help="more verbose parsing output (only errors by default)",
67 )
68 parser.add_argument(
69 "--future", "-u",
70 action="store_true",
71 help="use experimental features (currently optdone_as_list)",
72 )
73 parser.add_argument(
74 "--full", "-f",
75 action="store_true",
76 help="toggle full print behaviour for attributes",
77 )
79 args = parser.parse_args()
81 arglist = args.attribute_or_compchemlogfile
82 showattr = args.list
83 cjsonfile = args.json
84 multifile = args.multi
85 verbose = args.verbose
86 future = args.future
87 full = args.full
89 # Toggle full print behaviour for numpy arrays.
90 if full:
91 numpy.set_printoptions(threshold=numpy.inf)
93 # We need at least one attribute and the filename, so two arguments, or
94 # just one filename if we want to list attributes that can be extracted.
95 # In multifile mode, we generally want at least two filenames, so the
96 # expected number of arguments is a bit different.
97 if not multifile:
98 correct_number = (not showattr and len(arglist) > 1) or (showattr and len(arglist) > 0)
99 else:
100 correct_number = (not showattr and len(arglist) > 2) or (showattr and len(arglist) > 1)
101 if not correct_number:
102 print("The number of arguments does not seem to be correct.")
103 parser.print_usage()
104 parser.exit(1)
106 # Figure out which are the attribute names and which are the filenames or links.
107 # Note that in Linux, the shell expands wild cards, but not so in Windows,
108 # so try to do that here using glob.
109 attrnames = []
110 filenames = []
111 for arg in arglist:
112 if arg not in ccData._attrlist:
113 fuzzy_attr = difflib.get_close_matches(arg, ccData._attrlist, n=1, cutoff=0.85)
114 if len(fuzzy_attr) > 0:
115 fuzzy_attr = fuzzy_attr[0]
116 logging.warning("Attribute '{0}' not found, but attribute '{1}' is close. "
117 "Using '{1}' instead.".format(arg, fuzzy_attr))
118 arg = fuzzy_attr
119 if arg in ccData._attrlist:
120 attrnames.append(arg)
121 elif URL_PATTERN.match(arg) or os.path.isfile(arg):
122 filenames.append(arg)
123 else:
124 wildcardmatches = glob.glob(arg)
125 if wildcardmatches:
126 filenames.extend(wildcardmatches)
127 else:
128 print("%s is neither a filename nor an attribute name." % arg)
129 parser.print_usage()
130 parser.exit(1)
132 # Since there is some ambiguity to the correct number of arguments, check
133 # that there is at least one filename (or two in multifile mode), and also
134 # at least one attribute to parse if the -l option was not passed.
135 if len(filenames) == 0:
136 print("No logfiles given")
137 parser.exit(1)
138 if multifile and len(filenames) == 1:
139 print("Expecting at least two logfiles in multifile mode")
140 parser.exit(1)
141 if not showattr and len(attrnames) == 0:
142 print("No attributes given")
143 parser.exit(1)
145 # This should be sufficient to correctly handle multiple files, that is to
146 # run the loop below only once with all logfiles in the variable `filename`.
147 # Although, perhaps it would be clearer to abstract the contents of the loop
148 # into another function.
149 if multifile:
150 filenames = [filenames]
152 # Now parse each file and print out the requested attributes.
153 for filename in filenames:
155 if multifile:
156 name = ", ".join(filename[:-1]) + " and " + filename[-1]
157 else:
158 name = filename
160 # The keyword dictionary are not used so much. but could be useful for
161 # passing options downstream. For example, we might use --future for
162 # triggering experimental or alternative behavior (as with optdone).
163 kwargs = {}
164 if verbose:
165 kwargs['verbose'] = True
166 kwargs['loglevel'] = logging.INFO
167 else:
168 kwargs['verbose'] = False
169 kwargs['loglevel'] = logging.ERROR
170 if future:
171 kwargs['future'] = True
172 if cjsonfile:
173 kwargs['cjson'] = True
175 print("Attempting to read %s" % name)
176 data = ccread(filename, **kwargs)
178 if data is None:
179 print("Cannot figure out the format of '%s'" % name)
180 print("Report this to the cclib development team if you think it is an error.")
181 print("\n" + parser.format_usage())
182 parser.exit(1)
184 if showattr:
185 print("cclib can parse the following attributes from %s:" % name)
186 if cjsonfile:
187 for key in data:
188 print(key)
189 break
190 for attr in data._attrlist:
191 if hasattr(data, attr):
192 print(" %s" % attr)
193 else:
194 invalid = False
195 for attr in attrnames:
196 if cjsonfile:
197 if attr in data:
198 print("%s:\n%s" % (attr, data[attr]))
199 continue
200 else:
201 if hasattr(data, attr):
202 print(attr)
203 attr_val = getattr(data, attr)
204 # List of attributes to be printed with new lines
205 if attr in data._listsofarrays and full:
206 for val in attr_val:
207 pprint(val)
208 else:
209 pprint(attr_val)
210 continue
212 print("Could not parse %s from this file." % attr)
213 invalid = True
214 if invalid:
215 parser.print_help()
218if __name__ == "__main__":
220 ccget()