Repository: yt-dlp/yt-dlp
File: yt_dlp/YoutubeDL.py (Python)

Code before:

        color_policy = self.params.get('color') or {}
        self.params['color'] = {
            name: (
                'nocolor' if self.params.get('no_color') else
                color_policy if isinstance(color_policy, str) else
                color_policy.get(name) or 'auto')
            for name in ('screen', 'stdout', 'stderr')
        }
        term_allow_color = os.environ.get('TERM', '').lower() != 'dumb'

        def process_color_policy(name, stream):
            policy = self.params['color'].get(name)
            if policy == 'never':
                return False
            if policy == 'always':
                return True
            if policy == 'nocolor':
                return 'nocolor'
            return term_allow_color and supports_terminal_sequences(stream)

        self._allow_colors = Namespace(
            out=process_color_policy('stdout', self._out_files.out),
            error=process_color_policy('stderr', self._out_files.error),
            screen=process_color_policy('screen', self._out_files.screen),
        )

        # The code is left like this to be reused for future deprecations
        MIN_SUPPORTED, MIN_RECOMMENDED = (3, 7), (3, 7)
        current_version = sys.version_info[:2]
        if current_version < MIN_RECOMMENDED:
            msg = ('Support for Python version %d.%d has been deprecated. '
                   'See  https://github.com/yt-dlp/yt-dlp/issues/3764  for more details.'
                   '\n                    You will no longer receive updates on this version')
            if current_version < MIN_SUPPORTED:
                msg = 'Python version %d.%d is no longer supported'
            self.deprecated_feature(
                f'{msg}! Please update to Python %d.%d or above' % (*current_version, *MIN_RECOMMENDED))

        if self.params.get('allow_unplayable_formats'):
            self.report_warning(
                f'You have asked for {self._format_err("UNPLAYABLE", self.Styles.EMPHASIS)} formats to be listed/downloaded. '
                'This is a developer option intended for debugging. \n'
                '         If you experience any issues while using this option, '
                f'{self._format_err("DO NOT", self.Styles.ERROR)} open a bug report')

        if self.params.get('bidi_workaround', False):
            try:
                import pty
                master, slave = pty.openpty()
                width = shutil.get_terminal_size().columns

Review comment (suggestion):
I think there should only be `stderr` and `stdout` options and they should follow `sys.stdout`/`sys.stderr` and not what we internally call the fields. User is setting which of the output channels support color. So this would be like:



```suggestion
        if self.params.get('no_color'):
            if self.params.get('color'):
                self.report_warning(...)
            else:
                self.params['color'] = {'stdout': 'nocolor', 'stderr': 'nocolor'}
        term_allow_color = os.environ.get('TERM', '').lower() != 'dumb'
        _color_policies = {'never': False, 'always': True, 'nocolor': 'nocolor'}

        def process_color_policy(stream):
            policy = try_call(lambda: self.params['color'][stream.name[1:-1]])
            if policy in _color_policies:
                return _color_policies[policy]
            return term_allow_color and supports_terminal_sequences(stream)

        self._allow_colors = Namespace(**{
            name: process_color_policy(stream) for name, stream in self._out_files.items_
        })
```

Opinions?

---

With https://github.com/yt-dlp/yt-dlp/pull/6904#discussion_r1175717694


```suggestion
        if self.params.get('no_color'):
            if self.params.get('color'):
                self.report_warning(...)
            else:
                self.params['color'] = {'stdout': 'nocolor', 'stderr': 'nocolor'}
        term_allow_color = os.environ.get('TERM', '').lower() != 'dumb'

        def process_color_policy(stream):
            policy = try_call(lambda: self.params['color'][stream.name[1:-1]])
            if policy is None:
                return term_allow_color and supports_terminal_sequences(stream)
            assert policy in (True, False, 'nocolor')
            return policy

        self._allow_colors = Namespace(**{
            name: process_color_policy(stream) for name, stream in self._out_files.items_
        })
```


Code after:

        if self.params.get('no_color'):
            if self.params.get('color') is not None:
                self.report_warning('Overwriting params from "color" with "no_color"')
            self.params['color'] = 'no_color'

        term_allow_color = os.environ.get('TERM', '').lower() != 'dumb'

        def process_color_policy(stream):
            stream_name = {sys.stdout: 'stdout', sys.stderr: 'stderr'}[stream]
            policy = traverse_obj(self.params, ('color', (stream_name, None), {str}), get_all=False)
            if policy in ('auto', None):
                return term_allow_color and supports_terminal_sequences(stream)
            assert policy in ('always', 'never', 'no_color')
            return {'always': True, 'never': False}.get(policy, policy)

        self._allow_colors = Namespace(**{
            name: process_color_policy(stream)
            for name, stream in self._out_files.items_ if name != 'console'
        })

        # The code is left like this to be reused for future deprecations
        MIN_SUPPORTED, MIN_RECOMMENDED = (3, 7), (3, 7)
        current_version = sys.version_info[:2]
        if current_version < MIN_RECOMMENDED:
            msg = ('Support for Python version %d.%d has been deprecated. '
                   'See  https://github.com/yt-dlp/yt-dlp/issues/3764  for more details.'
                   '\n                    You will no longer receive updates on this version')
            if current_version < MIN_SUPPORTED:
                msg = 'Python version %d.%d is no longer supported'
            self.deprecated_feature(
                f'{msg}! Please update to Python %d.%d or above' % (*current_version, *MIN_RECOMMENDED))

        if self.params.get('allow_unplayable_formats'):
            self.report_warning(
                f'You have asked for {self._format_err("UNPLAYABLE", self.Styles.EMPHASIS)} formats to be listed/downloaded. '
                'This is a developer option intended for debugging. \n'
                '         If you experience any issues while using this option, '
                f'{self._format_err("DO NOT", self.Styles.ERROR)} open a bug report')

        if self.params.get('bidi_workaround', False):
            try:
                import pty
                master, slave = pty.openpty()
                width = shutil.get_terminal_size().columns
                width_args = [] if width is None else ['-w', str(width)]
                sp_kwargs = {'stdin': subprocess.PIPE, 'stdout': slave, 'stderr': self._out_files.error}
                try:
                    self._output_process = Popen(['bidiv'] + width_args, **sp_kwargs)
                except OSError:
                    self._output_process = Popen(['fribidi', '-c', 'UTF-8'] + width_args, **sp_kwargs)