summaryrefslogtreecommitdiff
path: root/qutebrowser/qutebrowser.py
blob: d0819f8328a07a46634c816f5c35f08bdccba733 (plain)
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
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:

# Copyright 2014-2021 Florian Bruhin (The Compiler) <mail@qutebrowser.org>

# This file is part of qutebrowser.
#
# qutebrowser is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# qutebrowser 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with qutebrowser.  If not, see <https://www.gnu.org/licenses/>.

"""Early initialization and main entry point.

qutebrowser's initialization process roughly looks like this:

- This file gets imported, either via the setuptools entry point or
  __main__.py.
- At import time, we check for the correct Python version and show an error if
  it's too old.
- The main() function in this file gets invoked
- Argument parsing takes place
- earlyinit.early_init() gets invoked to do various low-level initialization
  and checks whether all dependencies are met.
- app.run() gets called, which takes over.
  See the docstring of app.py for details.
"""

import sys
import json

import qutebrowser
try:
    from qutebrowser.misc.checkpyver import check_python_version
except ImportError:
    try:
        # python2
        from .misc.checkpyver import check_python_version
    except (SystemError, ValueError):
        # Import without module - SystemError on Python3, ValueError (?!?) on
        # Python2
        sys.stderr.write("Please don't run this script directly, do something "
                         "like   python3 -m qutebrowser   instead.\n")
        sys.stderr.flush()
        sys.exit(100)
check_python_version()

import argparse  # pylint: disable=wrong-import-order
from qutebrowser.misc import earlyinit


def get_argparser():
    """Get the argparse parser."""
    parser = argparse.ArgumentParser(prog='qutebrowser',
                                     description=qutebrowser.__description__)
    parser.add_argument('-B', '--basedir', help="Base directory for all "
                        "storage.")
    parser.add_argument('-C', '--config-py', help="Path to config.py.",
                        metavar='CONFIG')
    parser.add_argument('-V', '--version', help="Show version and quit.",
                        action='store_true')
    parser.add_argument('-s', '--set', help="Set a temporary setting for "
                        "this session.", nargs=2, action='append',
                        dest='temp_settings', default=[],
                        metavar=('OPTION', 'VALUE'))
    parser.add_argument('-r', '--restore', help="Restore a named session.",
                        dest='session')
    parser.add_argument('-R', '--override-restore', help="Don't restore a "
                        "session even if one would be restored.",
                        action='store_true')
    parser.add_argument('--target', choices=['auto', 'tab', 'tab-bg',
                                             'tab-silent', 'tab-bg-silent',
                                             'window', 'private-window'],
                        help="How URLs should be opened if there is already a "
                             "qutebrowser instance running.")
    parser.add_argument('--backend', choices=['webkit', 'webengine'],
                        help="Which backend to use.")
    parser.add_argument('--desktop-file-name',
                        default="org.qutebrowser.qutebrowser",
                        help="Set the base name of the desktop entry for this "
                        "application. Used to set the app_id under Wayland. See "
                        "https://doc.qt.io/qt-5/qguiapplication.html#desktopFileName-prop")

    parser.add_argument('--json-args', help=argparse.SUPPRESS)
    parser.add_argument('--temp-basedir-restarted',
                        help=argparse.SUPPRESS,
                        action='store_true')

    # WORKAROUND to be able to restart from older qutebrowser versions into this one.
    # Should be removed at some point.
    parser.add_argument('--enable-webengine-inspector',
                        help=argparse.SUPPRESS,
                        action='store_true')

    debug = parser.add_argument_group('debug arguments')
    debug.add_argument('-l', '--loglevel', dest='loglevel',
                       help="Override the configured console loglevel",
                       choices=['critical', 'error', 'warning', 'info',
                                'debug', 'vdebug'])
    debug.add_argument('--logfilter', type=logfilter_error,
                       help="Comma-separated list of things to be logged "
                       "to the debug log on stdout.")
    debug.add_argument('--loglines',
                       help="How many lines of the debug log to keep in RAM "
                       "(-1: unlimited).",
                       default=2000, type=int)
    debug.add_argument('-d', '--debug', help="Turn on debugging options.",
                       action='store_true')
    debug.add_argument('--json-logging', action='store_true', help="Output log"
                       " lines in JSON format (one object per line).")
    debug.add_argument('--nocolor', help="Turn off colored logging.",
                       action='store_false', dest='color')
    debug.add_argument('--force-color', help="Force colored logging",
                       action='store_true')
    debug.add_argument('--nowindow', action='store_true', help="Don't show "
                       "the main window.")
    debug.add_argument('-T', '--temp-basedir', action='store_true', help="Use "
                       "a temporary basedir.")
    debug.add_argument('--no-err-windows', action='store_true', help="Don't "
                       "show any error windows (used for tests/smoke.py).")
    debug.add_argument('--qt-arg', help="Pass an argument with a value to Qt. "
                       "For example, you can do "
                       "`--qt-arg geometry 650x555+200+300` to set the window "
                       "geometry.", nargs=2, metavar=('NAME', 'VALUE'),
                       action='append')
    debug.add_argument('--qt-flag', help="Pass an argument to Qt as flag.",
                       nargs=1, action='append')
    debug.add_argument('-D', '--debug-flag', type=debug_flag_error,
                       default=[], help="Pass name of debugging feature to be"
                       " turned on.", action='append', dest='debug_flags')
    parser.add_argument('command', nargs='*', help="Commands to execute on "
                        "startup.", metavar=':command')
    # URLs will actually be in command
    parser.add_argument('url', nargs='*', help="URLs to open on startup "
                        "(empty as a window separator).")
    return parser


def directory(arg):
    if not arg:
        raise argparse.ArgumentTypeError("Invalid empty value")


def logfilter_error(logfilter):
    """Validate logger names passed to --logfilter.

    Args:
        logfilter: A comma separated list of logger names.
    """
    from qutebrowser.utils import log
    try:
        log.LogFilter.parse(logfilter)
    except log.InvalidLogFilterError as e:
        raise argparse.ArgumentTypeError(e)
    return logfilter


def debug_flag_error(flag):
    """Validate flags passed to --debug-flag.

    Available flags:
        debug-exit: Turn on debugging of late exit.
        pdb-postmortem: Drop into pdb on exceptions.
        no-sql-history: Don't store history items.
        no-scroll-filtering: Process all scrolling updates.
        log-requests: Log all network requests.
        log-cookies: Log cookies in cookie filter.
        log-scroll-pos: Log all scrolling changes.
        log-sensitive-keys: Log keypresses in passthrough modes.
        stack: Enable Chromium stack logging.
        chromium: Enable Chromium logging.
        wait-renderer-process: Wait for debugger in renderer process.
        avoid-chromium-init: Enable `--version` without initializing Chromium.
        werror: Turn Python warnings into errors.
        test-notification-service: Use the testing libnotify service.
    """
    valid_flags = ['debug-exit', 'pdb-postmortem', 'no-sql-history',
                   'no-scroll-filtering', 'log-requests', 'log-cookies',
                   'log-scroll-pos', 'log-sensitive-keys', 'stack', 'chromium',
                   'wait-renderer-process', 'avoid-chromium-init', 'werror',
                   'test-notification-service']

    if flag in valid_flags:
        return flag
    else:
        raise argparse.ArgumentTypeError("Invalid debug flag - valid flags: {}"
                                         .format(', '.join(valid_flags)))


def _unpack_json_args(args):
    """Restore arguments from --json-args after a restart.

    When restarting, we serialize the argparse namespace into json, and
    construct a "fake" argparse.Namespace here based on the data loaded
    from json.
    """
    new_args = vars(args)
    data = json.loads(args.json_args)
    new_args.update(data)
    return argparse.Namespace(**new_args)


def main():
    parser = get_argparser()
    argv = sys.argv[1:]
    args = parser.parse_args(argv)
    if args.json_args is not None:
        args = _unpack_json_args(args)
    earlyinit.early_init(args)
    # We do this imports late as earlyinit needs to be run first (because of
    # version checking and other early initialization)
    from qutebrowser import app
    return app.run(args)