.. Copyright (C) 2001-2026 NLTK Project
.. For license information, see LICENSE.TXT

==========================================
 Unit tests for the nltk.utilities module
==========================================

overridden()
~~~~~~~~~~~~
    >>> from nltk.internals import overridden

The typical use case is in defining methods for an interface or
abstract base class, in such a way that subclasses don't have to
implement all of the methods:

    >>> class EaterI(object):
    ...     '''Subclass must define eat() or batch_eat().'''
    ...     def eat(self, food):
    ...         if overridden(self.batch_eat):
    ...             return self.batch_eat([food])[0]
    ...         else:
    ...             raise NotImplementedError()
    ...     def batch_eat(self, foods):
    ...         return [self.eat(food) for food in foods]

As long as a subclass implements one method, it will be used to
perform the other method:

    >>> class GoodEater1(EaterI):
    ...     def eat(self, food):
    ...         return 'yum'
    >>> GoodEater1().eat('steak')
    'yum'
    >>> GoodEater1().batch_eat(['steak', 'peas'])
    ['yum', 'yum']

    >>> class GoodEater2(EaterI):
    ...     def batch_eat(self, foods):
    ...         return ['yum' for food in foods]
    >>> GoodEater2().eat('steak')
    'yum'
    >>> GoodEater2().batch_eat(['steak', 'peas'])
    ['yum', 'yum']

But if a subclass doesn't implement either one, then they'll get an
error when they try to call them.  (nb this is better than infinite
recursion):

    >>> class BadEater1(EaterI):
    ...     pass
    >>> BadEater1().eat('steak')
    Traceback (most recent call last):
      . . .
    NotImplementedError
    >>> BadEater1().batch_eat(['steak', 'peas'])
    Traceback (most recent call last):
      . . .
    NotImplementedError

Trying to use the abstract base class itself will also result in an
error:

    >>> class EaterI(EaterI):
    ...     pass
    >>> EaterI().eat('steak')
    Traceback (most recent call last):
      . . .
    NotImplementedError
    >>> EaterI().batch_eat(['steak', 'peas'])
    Traceback (most recent call last):
      . . .
    NotImplementedError

It's ok to use intermediate abstract classes:

    >>> class AbstractEater(EaterI):
    ...     pass

    >>> class GoodEater3(AbstractEater):
    ...     def eat(self, food):
    ...         return 'yum'
    ...
    >>> GoodEater3().eat('steak')
    'yum'
    >>> GoodEater3().batch_eat(['steak', 'peas'])
    ['yum', 'yum']

    >>> class GoodEater4(AbstractEater):
    ...     def batch_eat(self, foods):
    ...         return ['yum' for food in foods]
    >>> GoodEater4().eat('steak')
    'yum'
    >>> GoodEater4().batch_eat(['steak', 'peas'])
    ['yum', 'yum']

    >>> class BadEater2(AbstractEater):
    ...     pass
    >>> BadEater2().eat('steak')
    Traceback (most recent call last):
      . . .
    NotImplementedError
    >>> BadEater2().batch_eat(['steak', 'peas'])
    Traceback (most recent call last):
      . . .
    NotImplementedError

Here's some extra tests:

    >>> class A(object):
    ...     def f(x): pass
    >>> class B(A):
    ...     def f(x): pass
    >>> class C(A): pass
    >>> class D(B): pass

    >>> overridden(A().f)
    False
    >>> overridden(B().f)
    True
    >>> overridden(C().f)
    False
    >>> overridden(D().f)
    True

It works for classic classes, too:

    >>> class A:
    ...     def f(x): pass
    >>> class B(A):
    ...     def f(x): pass
    >>> class C(A): pass
    >>> class D(B): pass
    >>> overridden(A().f)
    False
    >>> overridden(B().f)
    True
    >>> overridden(C().f)
    False
    >>> overridden(D().f)
    True


read_str()
~~~~~~~~~~~~
    >>> from nltk.internals import read_str

Test valid scenarios

    >>> read_str("'valid string'", 0)
    ('valid string', 14)

Now test invalid scenarios

    >>> read_str("should error", 0)
    Traceback (most recent call last):
    ...
    nltk.internals.ReadError: Expected open quote at 0
    >>> read_str("'should error", 0)
    Traceback (most recent call last):
    ...
    nltk.internals.ReadError: Expected close quote at 1

=====================================================
 _validate_java_options (CVE-2026-12841, CWE-88)
=====================================================

>>> from nltk.internals import _validate_java_options

Safe JVM tuning flags are permitted (no exception raised):

>>> _validate_java_options(["-Xmx512m"])
>>> _validate_java_options(["-Xms128m"])
>>> _validate_java_options(["-Xss4m"])

The legacy short aliases (-mx / -ms / -ss) that Stanford and CoreNLP
wrappers pass are equivalent and also permitted:

>>> _validate_java_options(["-mx2g"])
>>> _validate_java_options(["-ms128m"])
>>> _validate_java_options(["-ss4m"])
>>> _validate_java_options(["-XX:+UseG1GC"])
>>> _validate_java_options(["-verbose:gc"])
>>> _validate_java_options(["-server"])
>>> _validate_java_options(["-client"])
>>> _validate_java_options(["-Dsome.property=value"])
>>> _validate_java_options(["-Xmx1g", "-Dsome.key=val", "-server"])

Agent-loading and JDWP flags are blocked:

>>> _validate_java_options(["-agentlib:jdwp=transport=dt_socket,server=y"])  # doctest: +ELLIPSIS
Traceback (most recent call last):
    ...
ValueError: java_options contains a disallowed JVM/launcher flag: '-agentlib:jdwp=transport=dt_socket,server=y'...

>>> _validate_java_options(["-agentpath:/tmp/agent.so"])  # doctest: +ELLIPSIS
Traceback (most recent call last):
    ...
ValueError: java_options contains a disallowed JVM/launcher flag: '-agentpath:/tmp/agent.so'...

>>> _validate_java_options(["-javaagent:/tmp/agent.jar"])  # doctest: +ELLIPSIS
Traceback (most recent call last):
    ...
ValueError: java_options contains a disallowed JVM/launcher flag: '-javaagent:/tmp/agent.jar'...

>>> _validate_java_options(["-Xrunjdwp:transport=dt_socket"])  # doctest: +ELLIPSIS
Traceback (most recent call last):
    ...
ValueError: java_options contains a disallowed JVM/launcher flag: '-Xrunjdwp:transport=dt_socket'...

-jar and @argfile bypasses are also blocked:

>>> _validate_java_options(["-jar", "/tmp/evil.jar"])  # doctest: +ELLIPSIS
Traceback (most recent call last):
    ...
ValueError: java_options contains a disallowed JVM/launcher flag: '-jar'...

>>> _validate_java_options(["@evil.args"])  # doctest: +ELLIPSIS
Traceback (most recent call last):
    ...
ValueError: java_options contains a disallowed Java argument file reference: '@evil.args'...

Matching is case-insensitive:

>>> _validate_java_options(["-AGENTLIB:jdwp=transport=dt_socket"])  # doctest: +ELLIPSIS
Traceback (most recent call last):
    ...
ValueError: java_options contains a disallowed JVM/launcher flag: '-AGENTLIB:jdwp=transport=dt_socket'...
