File: ia.py

package info (click to toggle)
python-internetarchive 3.3.0-2~deb12u1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 1,096 kB
  • sloc: python: 6,276; xml: 180; makefile: 180
file content (183 lines) | stat: -rwxr-xr-x 6,068 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
#!/usr/bin/env python
#
# The internetarchive module is a Python/CLI interface to Archive.org.
#
# Copyright (C) 2012-2019 Internet Archive
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

"""A command line interface to Archive.org.

usage:
    ia [--help | --version]
    ia [--config-file FILE] [--log | --debug]
       [--insecure] [--host HOST] <command> [<args>]...

options:
    -h, --help
    -v, --version
    -c, --config-file FILE  Use FILE as config file. (Can also be set with the
                            IA_CONFIG_FILE environment variable. The option takes
                            precedence when both are used.)
    -l, --log               Turn on logging [default: False].
    -d, --debug             Turn on verbose logging [default: False].
    -i, --insecure          Use HTTP for all requests instead of HTTPS [default: false]
    -H, --host HOST         Host to use for requests (doesn't work for requests made to
                            s3.us.archive.org) [default: archive.org]

commands:
    help      Retrieve help for subcommands.
    configure Configure `ia`.
    metadata  Retrieve and modify metadata for items on Archive.org.
    upload    Upload items to Archive.org.
    download  Download files from Archive.org.
    delete    Delete files from Archive.org.
    search    Search Archive.org.
    tasks     Retrieve information about your Archive.org catalog tasks.
    list      List files in a given item.
    copy      Copy files in archive.org items.
    move      Move/rename files in archive.org items.
    reviews   Submit/modify reviews for archive.org items.

Documentation for 'ia' is available at:

    https://archive.org/services/docs/api/internetarchive/cli.html

See 'ia help <command>' for help on a specific command.
"""
from __future__ import annotations

import difflib
import errno
import os
import sys

from docopt import docopt, printable_usage
from pkg_resources import DistributionNotFound, iter_entry_points
from schema import Or, Schema, SchemaError  # type: ignore[import]

from internetarchive import __version__
from internetarchive.api import get_session
from internetarchive.utils import suppress_keyboard_interrupt_message

suppress_keyboard_interrupt_message()


cmd_aliases = {
    'co': 'configure',
    'md': 'metadata',
    'up': 'upload',
    'do': 'download',
    'rm': 'delete',
    'se': 'search',
    'ta': 'tasks',
    'ls': 'list',
    'cp': 'copy',
    'mv': 'move',
    're': 'reviews',
}


def load_ia_module(cmd: str):
    """Dynamically import ia module."""
    try:
        if cmd in list(cmd_aliases.keys()) + list(cmd_aliases.values()):
            _module = f'internetarchive.cli.ia_{cmd}'
            return __import__(_module, fromlist=['internetarchive.cli'])
        else:
            _module = f'ia_{cmd}'
            for ep in iter_entry_points('internetarchive.cli.plugins'):
                if ep.name == _module:
                    return ep.load()
            raise ImportError
    except (ImportError, DistributionNotFound):
        print(f"error: '{cmd}' is not an ia command! See 'ia help'",
              file=sys.stderr)
        matches = '\t'.join(difflib.get_close_matches(cmd, cmd_aliases.values()))
        if matches:
            print(f'\nDid you mean one of these?\n\t{matches}', file=sys.stderr)
        sys.exit(127)


def main() -> None:
    """This is the CLI driver for ia-wrapper."""
    args = docopt(__doc__, version=__version__, options_first=True)

    # Validate args.
    s = Schema({
        str: bool,
        '--config-file': Or(None, str),
        '--host': Or(None, str),
        '<args>': list,
        '<command>': Or(str, lambda _: 'help'),
    })
    try:
        args = s.validate(args)
    except SchemaError as exc:
        print(f'{exc}\n{printable_usage(__doc__)}', file=sys.stderr)
        sys.exit(1)

    # Get subcommand.
    cmd = args['<command>']
    if cmd in cmd_aliases:
        cmd = cmd_aliases[cmd]

    if (cmd == 'help') or (not cmd):
        if not args['<args>']:
            sys.exit(print(__doc__.strip(), file=sys.stderr))
        else:
            ia_module = load_ia_module(args['<args>'][0])
            sys.exit(print(ia_module.__doc__.strip(), file=sys.stderr))

    if cmd != 'configure' and args['--config-file']:
        if not os.path.isfile(args['--config-file']):
            print(f'--config-file should be a readable file.\n{printable_usage(__doc__)}',
                  file=sys.stderr)
            sys.exit(1)

    argv = [cmd] + args['<args>']

    config: dict[str, dict] = {}
    if args['--log']:
        config['logging'] = {'level': 'INFO'}
    elif args['--debug']:
        config['logging'] = {'level': 'DEBUG'}

    if args['--insecure']:
        config['general'] = {'secure': False}
    if args['--host']:
        if config.get('general'):
            config['general']['host'] = args['--host']
        else:
            config['general'] = {'host': args['--host']}

    session = get_session(config_file=args['--config-file'],
                          config=config,
                          debug=args['--debug'])

    ia_module = load_ia_module(cmd)
    try:
        sys.exit(ia_module.main(argv, session))
    except OSError as e:
        # Handle Broken Pipe errors.
        if e.errno == errno.EPIPE:
            sys.stderr.close()
            sys.stdout.close()
            sys.exit(0)
        else:
            raise


if __name__ == '__main__':
    main()