This file is indexed.

/usr/lib/python3/dist-packages/pymongo/common.py is in python3-pymongo 2.6.3-1build1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  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
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
# Copyright 2011-2012 10gen, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you
# may not use this file except in compliance with the License.  You
# may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.  See the License for the specific language governing
# permissions and limitations under the License.


"""Functions and classes common to multiple pymongo modules."""
import sys
import warnings
from pymongo import read_preferences

from pymongo.auth import MECHANISMS
from pymongo.read_preferences import ReadPreference
from pymongo.errors import ConfigurationError

HAS_SSL = True
try:
    import ssl
except ImportError:
    HAS_SSL = False


# Jython 2.7 includes an incomplete ssl module. See PYTHON-498.
if sys.platform.startswith('java'):
    HAS_SSL = False


def raise_config_error(key, dummy):
    """Raise ConfigurationError with the given key name."""
    raise ConfigurationError("Unknown option %s" % (key,))


def validate_boolean(option, value):
    """Validates that 'value' is 'true' or 'false'.
    """
    if isinstance(value, bool):
        return value
    elif isinstance(value, str):
        if value not in ('true', 'false'):
            raise ConfigurationError("The value of %s must be "
                                     "'true' or 'false'" % (option,))
        return value == 'true'
    raise TypeError("Wrong type for %s, value must be a boolean" % (option,))


def validate_integer(option, value):
    """Validates that 'value' is an integer (or basestring representation).
    """
    if isinstance(value, int):
        return value
    elif isinstance(value, str):
        if not value.isdigit():
            raise ConfigurationError("The value of %s must be "
                                     "an integer" % (option,))
        return int(value)
    raise TypeError("Wrong type for %s, value must be an integer" % (option,))


def validate_positive_integer(option, value):
    """Validate that 'value' is a positive integer.
    """
    val = validate_integer(option, value)
    if val < 0:
        raise ConfigurationError("The value of %s must be "
                                 "a positive integer" % (option,))
    return val


def validate_readable(option, value):
    """Validates that 'value' is file-like and readable.
    """
    # First make sure its a string py3.3 open(True, 'r') succeeds
    # Used in ssl cert checking due to poor ssl module error reporting
    value = validate_basestring(option, value)
    open(value, 'r').close()
    return value


def validate_cert_reqs(option, value):
    """Validate the cert reqs are valid. It must be None or one of the three
    values ``ssl.CERT_NONE``, ``ssl.CERT_OPTIONAL`` or ``ssl.CERT_REQUIRED``"""
    if value is None:
        return value
    if HAS_SSL:
        if value in (ssl.CERT_NONE, ssl.CERT_OPTIONAL, ssl.CERT_REQUIRED):
            return value
        raise ConfigurationError("The value of %s must be one of: "
                                 "`ssl.CERT_NONE`, `ssl.CERT_OPTIONAL` or "
                                 "`ssl.CERT_REQUIRED" % (option,))
    else:
        raise ConfigurationError("The value of %s is set but can't be "
                                 "validated. The ssl module is not available"
                                 % (option,))


def validate_positive_integer_or_none(option, value):
    """Validate that 'value' is a positive integer or None.
    """
    if value is None:
        return value
    return validate_positive_integer(option, value)


def validate_basestring(option, value):
    """Validates that 'value' is an instance of `basestring`.
    """
    if isinstance(value, str):
        return value
    raise TypeError("Wrong type for %s, value must be an "
                    "instance of %s" % (option, str.__name__))


def validate_int_or_basestring(option, value):
    """Validates that 'value' is an integer or string.
    """
    if isinstance(value, int):
        return value
    elif isinstance(value, str):
        if value.isdigit():
            return int(value)
        return value
    raise TypeError("Wrong type for %s, value must be an "
                    "integer or a string" % (option,))


def validate_positive_float(option, value):
    """Validates that 'value' is a float, or can be converted to one, and is
       positive.
    """
    err = ConfigurationError("%s must be a positive int or float" % (option,))
    try:
        value = float(value)
    except (ValueError, TypeError):
        raise err

    # float('inf') doesn't work in 2.4 or 2.5 on Windows, so just cap floats at
    # one billion - this is a reasonable approximation for infinity
    if not 0 < value < 1e9:
        raise err

    return value


def validate_timeout_or_none(option, value):
    """Validates a timeout specified in milliseconds returning
    a value in floating point seconds.
    """
    if value is None:
        return value
    return validate_positive_float(option, value) / 1000.0


def validate_read_preference(dummy, value):
    """Validate read preference for a ReplicaSetConnection.
    """
    if value in read_preferences.modes:
        return value

    # Also allow string form of enum for uri_parser
    try:
        return read_preferences.mongos_enum(value)
    except ValueError:
        raise ConfigurationError("Not a valid read preference")


def validate_tag_sets(dummy, value):
    """Validate tag sets for a ReplicaSetConnection.
    """
    if value is None:
        return [{}]

    if not isinstance(value, list):
        raise ConfigurationError((
            "Tag sets %s invalid, must be a list" ) % repr(value))
    if len(value) == 0:
        raise ConfigurationError((
            "Tag sets %s invalid, must be None or contain at least one set of"
            " tags") % repr(value))

    for tags in value:
        if not isinstance(tags, dict):
            raise ConfigurationError(
                "Tag set %s invalid, must be a dict" % repr(tags))

    return value


def validate_auth_mechanism(option, value):
    """Validate the authMechanism URI option.
    """
    if value not in MECHANISMS:
        raise ConfigurationError("%s must be in "
                                 "%s" % (option, MECHANISMS))
    return value


# jounal is an alias for j,
# wtimeoutms is an alias for wtimeout
VALIDATORS = {
    'replicaset': validate_basestring,
    'slaveok': validate_boolean,
    'slave_okay': validate_boolean,
    'safe': validate_boolean,
    'w': validate_int_or_basestring,
    'wtimeout': validate_integer,
    'wtimeoutms': validate_integer,
    'fsync': validate_boolean,
    'j': validate_boolean,
    'journal': validate_boolean,
    'connecttimeoutms': validate_timeout_or_none,
    'sockettimeoutms': validate_timeout_or_none,
    'waitqueuetimeoutms': validate_timeout_or_none,
    'waitqueuemultiple': validate_positive_integer_or_none,
    'ssl': validate_boolean,
    'ssl_keyfile': validate_readable,
    'ssl_certfile': validate_readable,
    'ssl_cert_reqs': validate_cert_reqs,
    'ssl_ca_certs': validate_readable,
    'readpreference': validate_read_preference,
    'read_preference': validate_read_preference,
    'tag_sets': validate_tag_sets,
    'secondaryacceptablelatencyms': validate_positive_float,
    'secondary_acceptable_latency_ms': validate_positive_float,
    'auto_start_request': validate_boolean,
    'use_greenlets': validate_boolean,
    'authmechanism': validate_auth_mechanism,
    'authsource': validate_basestring,
    'gssapiservicename': validate_basestring,
}


_AUTH_OPTIONS = frozenset(['gssapiservicename'])


def validate_auth_option(option, value):
    """Validate optional authentication parameters.
    """
    lower, value = validate(option, value)
    if lower not in _AUTH_OPTIONS:
        raise ConfigurationError('Unknown '
                                 'authentication option: %s' % (option,))
    return lower, value


def validate(option, value):
    """Generic validation function.
    """
    lower = option.lower()
    validator = VALIDATORS.get(lower, raise_config_error)
    value = validator(option, value)
    return lower, value


SAFE_OPTIONS = frozenset([
    'w',
    'wtimeout',
    'wtimeoutms',
    'fsync',
    'j',
    'journal'
])


class WriteConcern(dict):

    def __init__(self, *args, **kwargs):
        """A subclass of dict that overrides __setitem__ to
        validate write concern options.
        """
        super(WriteConcern, self).__init__(*args, **kwargs)

    def __setitem__(self, key, value):
        if key not in SAFE_OPTIONS:
            raise ConfigurationError("%s is not a valid write "
                                     "concern option." % (key,))
        key, value = validate(key, value)
        super(WriteConcern, self).__setitem__(key, value)


class BaseObject(object):
    """A base class that provides attributes and methods common
    to multiple pymongo classes.

    SHOULD NOT BE USED BY DEVELOPERS EXTERNAL TO 10GEN
    """

    def __init__(self, **options):

        self.__slave_okay = False
        self.__read_pref = ReadPreference.PRIMARY
        self.__tag_sets = [{}]
        self.__secondary_acceptable_latency_ms = 15
        self.__safe = None
        self.__write_concern = WriteConcern()
        self.__set_options(options)
        if (self.__read_pref == ReadPreference.PRIMARY
            and self.__tag_sets != [{}]
        ):
            raise ConfigurationError(
                "ReadPreference PRIMARY cannot be combined with tags")

        # If safe hasn't been implicitly set by write concerns then set it.
        if self.__safe is None:
            if options.get("w") == 0:
                self.__safe = False
            else:
                self.__safe = validate_boolean('safe', options.get("safe", True))
        # Note: 'safe' is always passed by Connection and ReplicaSetConnection
        # Always do the most "safe" thing, but warn about conflicts.
        if self.__safe and options.get('w') == 0:
            warnings.warn("Conflicting write concerns.  'w' set to 0 "
                          "but other options have enabled write concern. "
                          "Please set 'w' to a value other than 0.",
                          UserWarning)

    def __set_safe_option(self, option, value):
        """Validates and sets getlasterror options for this
        object (Connection, Database, Collection, etc.)
        """
        if value is None:
            self.__write_concern.pop(option, None)
        else:
            self.__write_concern[option] = value
            if option != "w" or value != 0:
                self.__safe = True

    def __set_options(self, options):
        """Validates and sets all options passed to this object."""
        for option, value in options.items():
            if option in ('slave_okay', 'slaveok'):
                self.__slave_okay = validate_boolean(option, value)
            elif option in ('read_preference', "readpreference"):
                self.__read_pref = validate_read_preference(option, value)
            elif option == 'tag_sets':
                self.__tag_sets = validate_tag_sets(option, value)
            elif option in (
                'secondaryacceptablelatencyms',
                'secondary_acceptable_latency_ms'
            ):
                self.__secondary_acceptable_latency_ms = \
                    validate_positive_float(option, value)
            elif option in SAFE_OPTIONS:
                if option == 'journal':
                    self.__set_safe_option('j', value)
                elif option == 'wtimeoutms':
                    self.__set_safe_option('wtimeout', value)
                else:
                    self.__set_safe_option(option, value)

    def __set_write_concern(self, value):
        """Property setter for write_concern."""
        if not isinstance(value, dict):
            raise ConfigurationError("write_concern must be an "
                                     "instance of dict or a subclass.")
        # Make a copy here to avoid users accidentally setting the
        # same dict on multiple instances.
        wc = WriteConcern()
        for k, v in value.items():
            # Make sure we validate each option.
            wc[k] = v
        self.__write_concern = wc

    def __get_write_concern(self):
        """The default write concern for this instance.

        Supports dict style access for getting/setting write concern
        options. Valid options include:

        - `w`: (integer or string) If this is a replica set, write operations
          will block until they have been replicated to the specified number
          or tagged set of servers. `w=<int>` always includes the replica set
          primary (e.g. w=3 means write to the primary and wait until
          replicated to **two** secondaries). **Setting w=0 disables write
          acknowledgement and all other write concern options.**
        - `wtimeout`: (integer) Used in conjunction with `w`. Specify a value
          in milliseconds to control how long to wait for write propagation
          to complete. If replication does not complete in the given
          timeframe, a timeout exception is raised.
        - `j`: If ``True`` block until write operations have been committed
          to the journal. Ignored if the server is running without journaling.
        - `fsync`: If ``True`` force the database to fsync all files before
          returning. When used with `j` the server awaits the next group
          commit before returning.

        >>> m = pymongo.MongoClient()
        >>> m.write_concern
        {}
        >>> m.write_concern = {'w': 2, 'wtimeout': 1000}
        >>> m.write_concern
        {'wtimeout': 1000, 'w': 2}
        >>> m.write_concern['j'] = True
        >>> m.write_concern
        {'wtimeout': 1000, 'j': True, 'w': 2}
        >>> m.write_concern = {'j': True}
        >>> m.write_concern
        {'j': True}
        >>> # Disable write acknowledgement and write concern
        ...
        >>> m.write_concern['w'] = 0


        .. note:: Accessing :attr:`write_concern` returns its value
           (a subclass of :class:`dict`), not a copy.

        .. warning:: If you are using :class:`~pymongo.connection.Connection`
           or :class:`~pymongo.replica_set_connection.ReplicaSetConnection`
           make sure you explicitly set ``w`` to 1 (or a greater value) or
           :attr:`safe` to ``True``. Unlike calling
           :meth:`set_lasterror_options`, setting an option in
           :attr:`write_concern` does not implicitly set :attr:`safe`
           to ``True``.
        """
        # To support dict style access we have to return the actual
        # WriteConcern here, not a copy.
        return self.__write_concern

    write_concern = property(__get_write_concern, __set_write_concern)

    def __get_slave_okay(self):
        """DEPRECATED. Use :attr:`read_preference` instead.

        .. versionchanged:: 2.1
           Deprecated slave_okay.
        .. versionadded:: 2.0
        """
        return self.__slave_okay

    def __set_slave_okay(self, value):
        """Property setter for slave_okay"""
        warnings.warn("slave_okay is deprecated. Please use "
                      "read_preference instead.", DeprecationWarning,
                      stacklevel=2)
        self.__slave_okay = validate_boolean('slave_okay', value)

    slave_okay = property(__get_slave_okay, __set_slave_okay)

    def __get_read_pref(self):
        """The read preference mode for this instance.

        See :class:`~pymongo.read_preferences.ReadPreference` for available options.

        .. versionadded:: 2.1
        """
        return self.__read_pref

    def __set_read_pref(self, value):
        """Property setter for read_preference"""
        self.__read_pref = validate_read_preference('read_preference', value)

    read_preference = property(__get_read_pref, __set_read_pref)

    def __get_acceptable_latency(self):
        """Any replica-set member whose ping time is within
        secondary_acceptable_latency_ms of the nearest member may accept
        reads. Defaults to 15 milliseconds.

        See :class:`~pymongo.read_preferences.ReadPreference`.

        .. versionadded:: 2.3

        .. note:: ``secondary_acceptable_latency_ms`` is ignored when talking to a
          replica set *through* a mongos. The equivalent is the localThreshold_ command
          line option.

        .. _localThreshold: http://docs.mongodb.org/manual/reference/mongos/#cmdoption-mongos--localThreshold
        """
        return self.__secondary_acceptable_latency_ms

    def __set_acceptable_latency(self, value):
        """Property setter for secondary_acceptable_latency_ms"""
        self.__secondary_acceptable_latency_ms = (validate_positive_float(
            'secondary_acceptable_latency_ms', value))

    secondary_acceptable_latency_ms = property(
        __get_acceptable_latency, __set_acceptable_latency)

    def __get_tag_sets(self):
        """Set ``tag_sets`` to a list of dictionaries like [{'dc': 'ny'}] to
        read only from members whose ``dc`` tag has the value ``"ny"``.
        To specify a priority-order for tag sets, provide a list of
        tag sets: ``[{'dc': 'ny'}, {'dc': 'la'}, {}]``. A final, empty tag
        set, ``{}``, means "read from any member that matches the mode,
        ignoring tags." ReplicaSetConnection tries each set of tags in turn
        until it finds a set of tags with at least one matching member.

           .. seealso:: `Data-Center Awareness
               <http://www.mongodb.org/display/DOCS/Data+Center+Awareness>`_

        .. versionadded:: 2.3
        """
        return self.__tag_sets

    def __set_tag_sets(self, value):
        """Property setter for tag_sets"""
        self.__tag_sets = validate_tag_sets('tag_sets', value)

    tag_sets = property(__get_tag_sets, __set_tag_sets)

    def __get_safe(self):
        """**DEPRECATED:** Use the 'w' :attr:`write_concern` option instead.

        Use getlasterror with every write operation?

        .. versionadded:: 2.0
        """
        return self.__safe

    def __set_safe(self, value):
        """Property setter for safe"""
        warnings.warn("safe is deprecated. Please use the"
                      " 'w' write_concern option instead.",
                      DeprecationWarning, stacklevel=2)
        self.__safe = validate_boolean('safe', value)

    safe = property(__get_safe, __set_safe)

    def get_lasterror_options(self):
        """DEPRECATED: Use :attr:`write_concern` instead.

        Returns a dict of the getlasterror options set on this instance.

        .. versionchanged:: 2.4
           Deprecated get_lasterror_options.
        .. versionadded:: 2.0
        """
        warnings.warn("get_lasterror_options is deprecated. Please use "
                      "write_concern instead.", DeprecationWarning,
                      stacklevel=2)
        return self.__write_concern.copy()

    def set_lasterror_options(self, **kwargs):
        """DEPRECATED: Use :attr:`write_concern` instead.

        Set getlasterror options for this instance.

        Valid options include j=<bool>, w=<int/string>, wtimeout=<int>,
        and fsync=<bool>. Implies safe=True.

        :Parameters:
            - `**kwargs`: Options should be passed as keyword
                          arguments (e.g. w=2, fsync=True)

        .. versionchanged:: 2.4
           Deprecated set_lasterror_options.
        .. versionadded:: 2.0
        """
        warnings.warn("set_lasterror_options is deprecated. Please use "
                      "write_concern instead.", DeprecationWarning,
                      stacklevel=2)
        for key, value in kwargs.items():
            self.__set_safe_option(key, value)

    def unset_lasterror_options(self, *options):
        """DEPRECATED: Use :attr:`write_concern` instead.

        Unset getlasterror options for this instance.

        If no options are passed unsets all getlasterror options.
        This does not set `safe` to False.

        :Parameters:
            - `*options`: The list of options to unset.

        .. versionchanged:: 2.4
           Deprecated unset_lasterror_options.
        .. versionadded:: 2.0
        """
        warnings.warn("unset_lasterror_options is deprecated. Please use "
                      "write_concern instead.", DeprecationWarning,
                      stacklevel=2)
        if len(options):
            for option in options:
                self.__write_concern.pop(option, None)
        else:
            self.__write_concern = WriteConcern()

    def _get_wc_override(self):
        """Get write concern override.

        Used in internal methods that **must** do acknowledged write ops.
        We don't want to override user write concern options if write concern
        is already enabled.
        """
        if self.safe and self.__write_concern.get('w') != 0:
            return {}
        return {'w': 1}

    def _get_write_mode(self, safe=None, **options):
        """Get the current write mode.

        Determines if the current write is safe or not based on the
        passed in or inherited safe value, write_concern values, or
        passed options.

        :Parameters:
            - `safe`: check that the operation succeeded?
            - `**options`: overriding write concern options.

        .. versionadded:: 2.3
        """
        # Don't ever send w=1 to the server.
        def pop1(dct):
            if dct.get('w') == 1:
                dct.pop('w')
            return dct

        if safe is not None:
            warnings.warn("The safe parameter is deprecated. Please use "
                          "write concern options instead.", DeprecationWarning,
                          stacklevel=3)
            validate_boolean('safe', safe)

        # Passed options override collection level defaults.
        if safe is not None or options:
            if safe or options:
                if not options:
                    options = self.__write_concern.copy()
                    # Backwards compatability edge case. Call getLastError
                    # with no options if safe=True was passed but collection
                    # level defaults have been disabled with w=0.
                    # These should be equivalent:
                    # Connection(w=0).foo.bar.insert({}, safe=True)
                    # MongoClient(w=0).foo.bar.insert({}, w=1)
                    if options.get('w') == 0:
                        return True, {}
                # Passing w=0 overrides passing safe=True.
                return options.get('w') != 0, pop1(options)
            return False, {}

        # Fall back to collection level defaults.
        # w=0 takes precedence over self.safe = True
        if self.__write_concern.get('w') == 0:
            return False, {}
        elif self.safe or self.__write_concern.get('w', 0) != 0:
            return True, pop1(self.__write_concern.copy())

        return False, {}