Esempio n. 1
0
 def setUp(self):
     fetch_cached = mock.patch(
         'beancount.prices.price.fetch_cached_price').start()
     fetch_cached.return_value = source.SourcePrice(
         D('125.00'), datetime.datetime(2015, 11, 22, 16, 0, 0), 'JPY')
     self.dprice = find_prices.DatedPrice('JPY', 'USD',
                                          datetime.date(2015, 11, 22), None)
     self.addCleanup(mock.patch.stopall)
Esempio n. 2
0
 def test_expressions(self):
     with test_utils.capture('stderr'):
         args, jobs, _, __ = test_utils.run_with_args(
             price.process_args, ['--no-cache', '-e', 'USD:yahoo/AAPL'])
         self.assertEqual([
             find_prices.DatedPrice(
                 'AAPL', 'USD', None,
                 [find_prices.PriceSource(yahoo, 'AAPL', False)])
         ], jobs)
Esempio n. 3
0
 def test_fetch_price__naive_time_no_timeozne(self, fetch_cached):
     fetch_cached.return_value = SourcePrice(
         D('125.00'), datetime.datetime(2015, 11, 22, 16, 0, 0), 'JPY')
     dprice = find_prices.DatedPrice('JPY', 'USD',
                                     datetime.date(2015, 11, 22), None)
     with self.assertRaises(ValueError):
         price.fetch_price(
             dprice._replace(
                 sources=[find_prices.PriceSource(yahoo, 'USDJPY', False)]),
             False)
Esempio n. 4
0
def process_args():
    """Process the arguments. This also initializes the logging module.

    Returns:
      A tuple of:
        args: The argparse receiver of command-line arguments.
        jobs: A list of DatedPrice job objects.
        entries: A list of all the parsed entries.
    """
    parser = version.ArgumentParser(
        description=beancount.prices.__doc__.splitlines()[0])

    # Input sources or filenames.
    parser.add_argument(
        'sources',
        nargs='+',
        help=('A list of filenames (or source "module/symbol", if -e is '
              'specified) from which to create a list of jobs.'))

    parser.add_argument(
        '-e',
        '--expressions',
        '--expression',
        action='store_true',
        help=('Interpret the arguments as "module/symbol" source strings.'))

    # Regular options.
    parser.add_argument(
        '-v',
        '--verbose',
        action='count',
        help=("Print out progress log. Specify twice for debugging info."))

    parser.add_argument(
        '-d',
        '--date',
        action='store',
        type=date_utils.parse_date_liberally,
        help=("Specify the date for which to fetch the prices."))

    parser.add_argument(
        '-i',
        '--inactive',
        action='store_true',
        help=
        ("Select all commodities from input files, not just the ones active on the date"
         ))

    parser.add_argument(
        '-u',
        '--undeclared',
        action='store',
        help=
        ("Include commodities viewed in the file even without a "
         "corresponding Commodity directive, from this default source. "
         "The currency name itself is used as the lookup symbol in this default source."
         ))

    parser.add_argument(
        '-c',
        '--clobber',
        action='store_true',
        help=
        ("Do not skip prices which are already present in input files; fetch them anyway."
         ))

    parser.add_argument(
        '-a',
        '--all',
        action='store_true',
        help=("A shorthand for --inactive, --undeclared, --clobber."))

    parser.add_argument(
        '-s',
        '--swap-inverted',
        action='store_true',
        help=
        ("For inverted sources, swap currencies instead of inverting the rate. "
         "For example, if fetching the rate for CAD from 'USD:google/^CURRENCY:USDCAD' "
         "results in 1.25, by default we would output \"price CAD  0.8000 USD\". "
         "Using this option we would instead output \" price USD   1.2500 CAD\"."
         ))

    parser.add_argument(
        '-n',
        '--dry-run',
        action='store_true',
        help=
        ("Don't actually fetch the prices, just print the list of the ones to be fetched."
         ))

    # Caching options.
    cache_group = parser.add_argument_group('cache')
    cache_filename = path.join(tempfile.gettempdir(),
                               "{}.cache".format(path.basename(sys.argv[0])))
    cache_group.add_argument(
        '--cache',
        dest='cache_filename',
        action='store',
        default=cache_filename,
        help="Enable the cache and with the given cache name.")
    cache_group.add_argument('--no-cache',
                             dest='cache_filename',
                             action='store_const',
                             const=None,
                             help="Disable the price cache.")

    cache_group.add_argument('--clear-cache',
                             action='store_true',
                             help="Clear the cache prior to startup")

    args = parser.parse_args()

    verbose_levels = {
        None: logging.WARN,
        0: logging.WARN,
        1: logging.INFO,
        2: logging.DEBUG
    }
    logging.basicConfig(level=verbose_levels[args.verbose],
                        format='%(levelname)-8s: %(message)s')

    if args.all:
        args.inactive = args.clobber = True
        args.undeclared = DEFAULT_SOURCE

    # Setup for processing.
    setup_cache(args.cache_filename, args.clear_cache)

    # Get the list of DatedPrice jobs to get from the arguments.
    logging.info("Processing at date: %s", args.date or datetime.date.today())
    jobs = []
    all_entries = []
    dcontext = None
    if args.expressions:
        # Interpret the arguments as price sources.
        for source_str in args.sources:
            psources = []
            try:
                psource_map = find_prices.parse_source_map(source_str)
            except ValueError:
                extra = "; did you provide a filename?" if path.exists(
                    source_str) else ''
                msg = ('Invalid source "{{}}"{}. '.format(extra) +
                       'Supported format is "CCY:module/SYMBOL"')
                parser.error(msg.format(source_str))
            else:
                for currency, psources in psource_map.items():
                    jobs.append(
                        find_prices.DatedPrice(psources[0].symbol, currency,
                                               args.date, psources))
    else:
        # Interpret the arguments as Beancount input filenames.
        for filename in args.sources:
            if not path.exists(filename) or not path.isfile(filename):
                parser.error('File does not exist: "{}"; '
                             'did you mean to use -e?'.format(filename))
                continue
            logging.info('Loading "%s"', filename)
            entries, errors, options_map = loader.load_file(
                filename, log_errors=sys.stderr)
            if dcontext is None:
                dcontext = options_map['dcontext']
            jobs.extend(
                find_prices.get_price_jobs_at_date(entries, args.date,
                                                   args.inactive,
                                                   args.undeclared))
            all_entries.extend(entries)

    return args, jobs, data.sorted(all_entries), dcontext