rustpython_vm/builtins/
bytes.rs

1use super::{
2    PositionIterInternal, PyDictRef, PyIntRef, PyStrRef, PyTuple, PyTupleRef, PyType, PyTypeRef,
3};
4use crate::{
5    anystr::{self, AnyStr},
6    atomic_func,
7    bytesinner::{
8        bytes_decode, ByteInnerFindOptions, ByteInnerNewOptions, ByteInnerPaddingOptions,
9        ByteInnerSplitOptions, ByteInnerTranslateOptions, DecodeArgs, PyBytesInner,
10    },
11    class::PyClassImpl,
12    common::{hash::PyHash, lock::PyMutex},
13    convert::{ToPyObject, ToPyResult},
14    function::{
15        ArgBytesLike, ArgIndex, ArgIterable, Either, OptionalArg, OptionalOption, PyComparisonValue,
16    },
17    protocol::{
18        BufferDescriptor, BufferMethods, PyBuffer, PyIterReturn, PyMappingMethods, PyNumberMethods,
19        PySequenceMethods,
20    },
21    sliceable::{SequenceIndex, SliceableSequenceOp},
22    types::{
23        AsBuffer, AsMapping, AsNumber, AsSequence, Callable, Comparable, Constructor, Hashable,
24        IterNext, Iterable, PyComparisonOp, Representable, SelfIter, Unconstructible,
25    },
26    AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult,
27    TryFromBorrowedObject, TryFromObject, VirtualMachine,
28};
29use bstr::ByteSlice;
30use once_cell::sync::Lazy;
31use std::{mem::size_of, ops::Deref};
32
33#[pyclass(module = false, name = "bytes")]
34#[derive(Clone, Debug)]
35pub struct PyBytes {
36    inner: PyBytesInner,
37}
38
39pub type PyBytesRef = PyRef<PyBytes>;
40
41impl From<Vec<u8>> for PyBytes {
42    fn from(elements: Vec<u8>) -> Self {
43        Self {
44            inner: PyBytesInner { elements },
45        }
46    }
47}
48
49impl From<PyBytesInner> for PyBytes {
50    fn from(inner: PyBytesInner) -> Self {
51        Self { inner }
52    }
53}
54
55impl ToPyObject for Vec<u8> {
56    fn to_pyobject(self, vm: &VirtualMachine) -> PyObjectRef {
57        vm.ctx.new_bytes(self).into()
58    }
59}
60
61impl Deref for PyBytes {
62    type Target = [u8];
63
64    fn deref(&self) -> &[u8] {
65        self.as_bytes()
66    }
67}
68
69impl AsRef<[u8]> for PyBytes {
70    fn as_ref(&self) -> &[u8] {
71        self.as_bytes()
72    }
73}
74impl AsRef<[u8]> for PyBytesRef {
75    fn as_ref(&self) -> &[u8] {
76        self.as_bytes()
77    }
78}
79
80impl PyPayload for PyBytes {
81    fn class(ctx: &Context) -> &'static Py<PyType> {
82        ctx.types.bytes_type
83    }
84}
85
86pub(crate) fn init(context: &Context) {
87    PyBytes::extend_class(context, context.types.bytes_type);
88    PyBytesIterator::extend_class(context, context.types.bytes_iterator_type);
89}
90
91impl Constructor for PyBytes {
92    type Args = ByteInnerNewOptions;
93
94    fn py_new(cls: PyTypeRef, options: Self::Args, vm: &VirtualMachine) -> PyResult {
95        options.get_bytes(cls, vm).to_pyresult(vm)
96    }
97}
98
99impl PyBytes {
100    pub fn new_ref(data: Vec<u8>, ctx: &Context) -> PyRef<Self> {
101        PyRef::new_ref(Self::from(data), ctx.types.bytes_type.to_owned(), None)
102    }
103
104    fn _getitem(&self, needle: &PyObject, vm: &VirtualMachine) -> PyResult {
105        match SequenceIndex::try_from_borrowed_object(vm, needle, "byte")? {
106            SequenceIndex::Int(i) => self
107                .getitem_by_index(vm, i)
108                .map(|x| vm.ctx.new_int(x).into()),
109            SequenceIndex::Slice(slice) => self
110                .getitem_by_slice(vm, slice)
111                .map(|x| vm.ctx.new_bytes(x).into()),
112        }
113    }
114}
115
116impl PyRef<PyBytes> {
117    fn repeat(self, count: isize, vm: &VirtualMachine) -> PyResult<PyRef<PyBytes>> {
118        if count == 1 && self.class().is(vm.ctx.types.bytes_type) {
119            // Special case: when some `bytes` is multiplied by `1`,
120            // nothing really happens, we need to return an object itself
121            // with the same `id()` to be compatible with CPython.
122            // This only works for `bytes` itself, not its subclasses.
123            return Ok(self);
124        }
125        self.inner
126            .mul(count, vm)
127            .map(|x| PyBytes::from(x).into_ref(&vm.ctx))
128    }
129}
130
131#[pyclass(
132    flags(BASETYPE),
133    with(
134        Py,
135        PyRef,
136        AsMapping,
137        AsSequence,
138        Hashable,
139        Comparable,
140        AsBuffer,
141        Iterable,
142        Constructor,
143        AsNumber,
144        Representable,
145    )
146)]
147impl PyBytes {
148    #[pymethod(magic)]
149    #[inline]
150    pub fn len(&self) -> usize {
151        self.inner.len()
152    }
153
154    #[inline]
155    pub fn is_empty(&self) -> bool {
156        self.inner.is_empty()
157    }
158
159    #[inline]
160    pub fn as_bytes(&self) -> &[u8] {
161        self.inner.as_bytes()
162    }
163
164    #[pymethod(magic)]
165    fn sizeof(&self) -> usize {
166        size_of::<Self>() + self.len() * size_of::<u8>()
167    }
168
169    #[pymethod(magic)]
170    fn add(&self, other: ArgBytesLike) -> Vec<u8> {
171        self.inner.add(&other.borrow_buf())
172    }
173
174    #[pymethod(magic)]
175    fn contains(
176        &self,
177        needle: Either<PyBytesInner, PyIntRef>,
178        vm: &VirtualMachine,
179    ) -> PyResult<bool> {
180        self.inner.contains(needle, vm)
181    }
182
183    #[pystaticmethod]
184    fn maketrans(from: PyBytesInner, to: PyBytesInner, vm: &VirtualMachine) -> PyResult<Vec<u8>> {
185        PyBytesInner::maketrans(from, to, vm)
186    }
187
188    #[pymethod(magic)]
189    fn getitem(&self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult {
190        self._getitem(&needle, vm)
191    }
192
193    #[pymethod]
194    fn isalnum(&self) -> bool {
195        self.inner.isalnum()
196    }
197
198    #[pymethod]
199    fn isalpha(&self) -> bool {
200        self.inner.isalpha()
201    }
202
203    #[pymethod]
204    fn isascii(&self) -> bool {
205        self.inner.isascii()
206    }
207
208    #[pymethod]
209    fn isdigit(&self) -> bool {
210        self.inner.isdigit()
211    }
212
213    #[pymethod]
214    fn islower(&self) -> bool {
215        self.inner.islower()
216    }
217
218    #[pymethod]
219    fn isspace(&self) -> bool {
220        self.inner.isspace()
221    }
222
223    #[pymethod]
224    fn isupper(&self) -> bool {
225        self.inner.isupper()
226    }
227
228    #[pymethod]
229    fn istitle(&self) -> bool {
230        self.inner.istitle()
231    }
232
233    #[pymethod]
234    fn lower(&self) -> Self {
235        self.inner.lower().into()
236    }
237
238    #[pymethod]
239    fn upper(&self) -> Self {
240        self.inner.upper().into()
241    }
242
243    #[pymethod]
244    fn capitalize(&self) -> Self {
245        self.inner.capitalize().into()
246    }
247
248    #[pymethod]
249    fn swapcase(&self) -> Self {
250        self.inner.swapcase().into()
251    }
252
253    #[pymethod]
254    pub(crate) fn hex(
255        &self,
256        sep: OptionalArg<Either<PyStrRef, PyBytesRef>>,
257        bytes_per_sep: OptionalArg<isize>,
258        vm: &VirtualMachine,
259    ) -> PyResult<String> {
260        self.inner.hex(sep, bytes_per_sep, vm)
261    }
262
263    #[pyclassmethod]
264    fn fromhex(cls: PyTypeRef, string: PyStrRef, vm: &VirtualMachine) -> PyResult {
265        let bytes = PyBytesInner::fromhex(string.as_str(), vm)?;
266        let bytes = vm.ctx.new_bytes(bytes).into();
267        PyType::call(&cls, vec![bytes].into(), vm)
268    }
269
270    #[pymethod]
271    fn center(&self, options: ByteInnerPaddingOptions, vm: &VirtualMachine) -> PyResult<PyBytes> {
272        Ok(self.inner.center(options, vm)?.into())
273    }
274
275    #[pymethod]
276    fn ljust(&self, options: ByteInnerPaddingOptions, vm: &VirtualMachine) -> PyResult<PyBytes> {
277        Ok(self.inner.ljust(options, vm)?.into())
278    }
279
280    #[pymethod]
281    fn rjust(&self, options: ByteInnerPaddingOptions, vm: &VirtualMachine) -> PyResult<PyBytes> {
282        Ok(self.inner.rjust(options, vm)?.into())
283    }
284
285    #[pymethod]
286    fn count(&self, options: ByteInnerFindOptions, vm: &VirtualMachine) -> PyResult<usize> {
287        self.inner.count(options, vm)
288    }
289
290    #[pymethod]
291    fn join(&self, iter: ArgIterable<PyBytesInner>, vm: &VirtualMachine) -> PyResult<PyBytes> {
292        Ok(self.inner.join(iter, vm)?.into())
293    }
294
295    #[pymethod]
296    fn endswith(&self, options: anystr::StartsEndsWithArgs, vm: &VirtualMachine) -> PyResult<bool> {
297        let (affix, substr) =
298            match options.prepare(self.as_bytes(), self.len(), |s, r| s.get_bytes(r)) {
299                Some(x) => x,
300                None => return Ok(false),
301            };
302        substr.py_startsendswith(
303            &affix,
304            "endswith",
305            "bytes",
306            |s, x: PyBytesInner| s.ends_with(x.as_bytes()),
307            vm,
308        )
309    }
310
311    #[pymethod]
312    fn startswith(
313        &self,
314        options: anystr::StartsEndsWithArgs,
315        vm: &VirtualMachine,
316    ) -> PyResult<bool> {
317        let (affix, substr) =
318            match options.prepare(self.as_bytes(), self.len(), |s, r| s.get_bytes(r)) {
319                Some(x) => x,
320                None => return Ok(false),
321            };
322        substr.py_startsendswith(
323            &affix,
324            "startswith",
325            "bytes",
326            |s, x: PyBytesInner| s.starts_with(x.as_bytes()),
327            vm,
328        )
329    }
330
331    #[pymethod]
332    fn find(&self, options: ByteInnerFindOptions, vm: &VirtualMachine) -> PyResult<isize> {
333        let index = self.inner.find(options, |h, n| h.find(n), vm)?;
334        Ok(index.map_or(-1, |v| v as isize))
335    }
336
337    #[pymethod]
338    fn index(&self, options: ByteInnerFindOptions, vm: &VirtualMachine) -> PyResult<usize> {
339        let index = self.inner.find(options, |h, n| h.find(n), vm)?;
340        index.ok_or_else(|| vm.new_value_error("substring not found".to_owned()))
341    }
342
343    #[pymethod]
344    fn rfind(&self, options: ByteInnerFindOptions, vm: &VirtualMachine) -> PyResult<isize> {
345        let index = self.inner.find(options, |h, n| h.rfind(n), vm)?;
346        Ok(index.map_or(-1, |v| v as isize))
347    }
348
349    #[pymethod]
350    fn rindex(&self, options: ByteInnerFindOptions, vm: &VirtualMachine) -> PyResult<usize> {
351        let index = self.inner.find(options, |h, n| h.rfind(n), vm)?;
352        index.ok_or_else(|| vm.new_value_error("substring not found".to_owned()))
353    }
354
355    #[pymethod]
356    fn translate(
357        &self,
358        options: ByteInnerTranslateOptions,
359        vm: &VirtualMachine,
360    ) -> PyResult<PyBytes> {
361        Ok(self.inner.translate(options, vm)?.into())
362    }
363
364    #[pymethod]
365    fn strip(&self, chars: OptionalOption<PyBytesInner>) -> Self {
366        self.inner.strip(chars).into()
367    }
368
369    #[pymethod]
370    fn removeprefix(&self, prefix: PyBytesInner) -> Self {
371        self.inner.removeprefix(prefix).into()
372    }
373
374    #[pymethod]
375    fn removesuffix(&self, suffix: PyBytesInner) -> Self {
376        self.inner.removesuffix(suffix).into()
377    }
378
379    #[pymethod]
380    fn split(
381        &self,
382        options: ByteInnerSplitOptions,
383        vm: &VirtualMachine,
384    ) -> PyResult<Vec<PyObjectRef>> {
385        self.inner
386            .split(options, |s, vm| vm.ctx.new_bytes(s.to_vec()).into(), vm)
387    }
388
389    #[pymethod]
390    fn rsplit(
391        &self,
392        options: ByteInnerSplitOptions,
393        vm: &VirtualMachine,
394    ) -> PyResult<Vec<PyObjectRef>> {
395        self.inner
396            .rsplit(options, |s, vm| vm.ctx.new_bytes(s.to_vec()).into(), vm)
397    }
398
399    #[pymethod]
400    fn partition(&self, sep: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyTupleRef> {
401        let sub = PyBytesInner::try_from_borrowed_object(vm, &sep)?;
402        let (front, has_mid, back) = self.inner.partition(&sub, vm)?;
403        Ok(vm.new_tuple((
404            vm.ctx.new_bytes(front),
405            if has_mid {
406                sep
407            } else {
408                vm.ctx.new_bytes(Vec::new()).into()
409            },
410            vm.ctx.new_bytes(back),
411        )))
412    }
413
414    #[pymethod]
415    fn rpartition(&self, sep: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyTupleRef> {
416        let sub = PyBytesInner::try_from_borrowed_object(vm, &sep)?;
417        let (back, has_mid, front) = self.inner.rpartition(&sub, vm)?;
418        Ok(vm.new_tuple((
419            vm.ctx.new_bytes(front),
420            if has_mid {
421                sep
422            } else {
423                vm.ctx.new_bytes(Vec::new()).into()
424            },
425            vm.ctx.new_bytes(back),
426        )))
427    }
428
429    #[pymethod]
430    fn expandtabs(&self, options: anystr::ExpandTabsArgs) -> Self {
431        self.inner.expandtabs(options).into()
432    }
433
434    #[pymethod]
435    fn splitlines(&self, options: anystr::SplitLinesArgs, vm: &VirtualMachine) -> Vec<PyObjectRef> {
436        self.inner
437            .splitlines(options, |x| vm.ctx.new_bytes(x.to_vec()).into())
438    }
439
440    #[pymethod]
441    fn zfill(&self, width: isize) -> Self {
442        self.inner.zfill(width).into()
443    }
444
445    #[pymethod]
446    fn replace(
447        &self,
448        old: PyBytesInner,
449        new: PyBytesInner,
450        count: OptionalArg<isize>,
451        vm: &VirtualMachine,
452    ) -> PyResult<PyBytes> {
453        Ok(self.inner.replace(old, new, count, vm)?.into())
454    }
455
456    #[pymethod]
457    fn title(&self) -> Self {
458        self.inner.title().into()
459    }
460
461    #[pymethod(name = "__rmul__")]
462    #[pymethod(magic)]
463    fn mul(zelf: PyRef<Self>, value: ArgIndex, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
464        zelf.repeat(value.try_to_primitive(vm)?, vm)
465    }
466
467    #[pymethod(name = "__mod__")]
468    fn mod_(&self, values: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyBytes> {
469        let formatted = self.inner.cformat(values, vm)?;
470        Ok(formatted.into())
471    }
472
473    #[pymethod(magic)]
474    fn rmod(&self, _values: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
475        vm.ctx.not_implemented()
476    }
477
478    #[pymethod(magic)]
479    fn getnewargs(&self, vm: &VirtualMachine) -> PyTupleRef {
480        let param: Vec<PyObjectRef> = self.elements().map(|x| x.to_pyobject(vm)).collect();
481        PyTuple::new_ref(param, &vm.ctx)
482    }
483}
484
485#[pyclass]
486impl Py<PyBytes> {
487    #[pymethod(magic)]
488    fn reduce_ex(
489        &self,
490        _proto: usize,
491        vm: &VirtualMachine,
492    ) -> (PyTypeRef, PyTupleRef, Option<PyDictRef>) {
493        Self::reduce(self, vm)
494    }
495
496    #[pymethod(magic)]
497    fn reduce(&self, vm: &VirtualMachine) -> (PyTypeRef, PyTupleRef, Option<PyDictRef>) {
498        let bytes = PyBytes::from(self.to_vec()).to_pyobject(vm);
499        (
500            self.class().to_owned(),
501            PyTuple::new_ref(vec![bytes], &vm.ctx),
502            self.as_object().dict(),
503        )
504    }
505}
506
507#[pyclass]
508impl PyRef<PyBytes> {
509    #[pymethod(magic)]
510    fn bytes(self, vm: &VirtualMachine) -> PyRef<PyBytes> {
511        if self.is(vm.ctx.types.bytes_type) {
512            self
513        } else {
514            PyBytes::from(self.inner.clone()).into_ref(&vm.ctx)
515        }
516    }
517
518    #[pymethod]
519    fn lstrip(self, chars: OptionalOption<PyBytesInner>, vm: &VirtualMachine) -> PyRef<PyBytes> {
520        let stripped = self.inner.lstrip(chars);
521        if stripped == self.as_bytes() {
522            self
523        } else {
524            vm.ctx.new_bytes(stripped.to_vec())
525        }
526    }
527
528    #[pymethod]
529    fn rstrip(self, chars: OptionalOption<PyBytesInner>, vm: &VirtualMachine) -> PyRef<PyBytes> {
530        let stripped = self.inner.rstrip(chars);
531        if stripped == self.as_bytes() {
532            self
533        } else {
534            vm.ctx.new_bytes(stripped.to_vec())
535        }
536    }
537
538    /// Return a string decoded from the given bytes.
539    /// Default encoding is 'utf-8'.
540    /// Default errors is 'strict', meaning that encoding errors raise a UnicodeError.
541    /// Other possible values are 'ignore', 'replace'
542    /// For a list of possible encodings,
543    /// see https://docs.python.org/3/library/codecs.html#standard-encodings
544    /// currently, only 'utf-8' and 'ascii' emplemented
545    #[pymethod]
546    fn decode(self, args: DecodeArgs, vm: &VirtualMachine) -> PyResult<PyStrRef> {
547        bytes_decode(self.into(), args, vm)
548    }
549}
550
551static BUFFER_METHODS: BufferMethods = BufferMethods {
552    obj_bytes: |buffer| buffer.obj_as::<PyBytes>().as_bytes().into(),
553    obj_bytes_mut: |_| panic!(),
554    release: |_| {},
555    retain: |_| {},
556};
557
558impl AsBuffer for PyBytes {
559    fn as_buffer(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<PyBuffer> {
560        let buf = PyBuffer::new(
561            zelf.to_owned().into(),
562            BufferDescriptor::simple(zelf.len(), true),
563            &BUFFER_METHODS,
564        );
565        Ok(buf)
566    }
567}
568
569impl AsMapping for PyBytes {
570    fn as_mapping() -> &'static PyMappingMethods {
571        static AS_MAPPING: Lazy<PyMappingMethods> = Lazy::new(|| PyMappingMethods {
572            length: atomic_func!(|mapping, _vm| Ok(PyBytes::mapping_downcast(mapping).len())),
573            subscript: atomic_func!(
574                |mapping, needle, vm| PyBytes::mapping_downcast(mapping)._getitem(needle, vm)
575            ),
576            ..PyMappingMethods::NOT_IMPLEMENTED
577        });
578        &AS_MAPPING
579    }
580}
581
582impl AsSequence for PyBytes {
583    fn as_sequence() -> &'static PySequenceMethods {
584        static AS_SEQUENCE: Lazy<PySequenceMethods> = Lazy::new(|| PySequenceMethods {
585            length: atomic_func!(|seq, _vm| Ok(PyBytes::sequence_downcast(seq).len())),
586            concat: atomic_func!(|seq, other, vm| {
587                PyBytes::sequence_downcast(seq)
588                    .inner
589                    .concat(other, vm)
590                    .map(|x| vm.ctx.new_bytes(x).into())
591            }),
592            repeat: atomic_func!(|seq, n, vm| {
593                let zelf = seq.obj.to_owned().downcast::<PyBytes>().map_err(|_| {
594                    vm.new_type_error("bad argument type for built-in operation".to_owned())
595                })?;
596                zelf.repeat(n, vm).to_pyresult(vm)
597            }),
598            item: atomic_func!(|seq, i, vm| {
599                PyBytes::sequence_downcast(seq)
600                    .as_bytes()
601                    .getitem_by_index(vm, i)
602                    .map(|x| vm.ctx.new_bytes(vec![x]).into())
603            }),
604            contains: atomic_func!(|seq, other, vm| {
605                let other =
606                    <Either<PyBytesInner, PyIntRef>>::try_from_object(vm, other.to_owned())?;
607                PyBytes::sequence_downcast(seq).contains(other, vm)
608            }),
609            ..PySequenceMethods::NOT_IMPLEMENTED
610        });
611        &AS_SEQUENCE
612    }
613}
614
615impl AsNumber for PyBytes {
616    fn as_number() -> &'static PyNumberMethods {
617        static AS_NUMBER: PyNumberMethods = PyNumberMethods {
618            remainder: Some(|a, b, vm| {
619                if let Some(a) = a.downcast_ref::<PyBytes>() {
620                    a.mod_(b.to_owned(), vm).to_pyresult(vm)
621                } else {
622                    Ok(vm.ctx.not_implemented())
623                }
624            }),
625            ..PyNumberMethods::NOT_IMPLEMENTED
626        };
627        &AS_NUMBER
628    }
629}
630
631impl Hashable for PyBytes {
632    #[inline]
633    fn hash(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyHash> {
634        Ok(zelf.inner.hash(vm))
635    }
636}
637
638impl Comparable for PyBytes {
639    fn cmp(
640        zelf: &Py<Self>,
641        other: &PyObject,
642        op: PyComparisonOp,
643        vm: &VirtualMachine,
644    ) -> PyResult<PyComparisonValue> {
645        Ok(if let Some(res) = op.identical_optimization(zelf, other) {
646            res.into()
647        } else if other.fast_isinstance(vm.ctx.types.memoryview_type)
648            && op != PyComparisonOp::Eq
649            && op != PyComparisonOp::Ne
650        {
651            return Err(vm.new_type_error(format!(
652                "'{}' not supported between instances of '{}' and '{}'",
653                op.operator_token(),
654                zelf.class().name(),
655                other.class().name()
656            )));
657        } else {
658            zelf.inner.cmp(other, op, vm)
659        })
660    }
661}
662
663impl Iterable for PyBytes {
664    fn iter(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult {
665        Ok(PyBytesIterator {
666            internal: PyMutex::new(PositionIterInternal::new(zelf, 0)),
667        }
668        .into_pyobject(vm))
669    }
670}
671
672impl Representable for PyBytes {
673    #[inline]
674    fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
675        zelf.inner.repr_bytes(vm)
676    }
677}
678
679#[pyclass(module = false, name = "bytes_iterator")]
680#[derive(Debug)]
681pub struct PyBytesIterator {
682    internal: PyMutex<PositionIterInternal<PyBytesRef>>,
683}
684
685impl PyPayload for PyBytesIterator {
686    fn class(ctx: &Context) -> &'static Py<PyType> {
687        ctx.types.bytes_iterator_type
688    }
689}
690
691#[pyclass(with(Unconstructible, IterNext, Iterable))]
692impl PyBytesIterator {
693    #[pymethod(magic)]
694    fn length_hint(&self) -> usize {
695        self.internal.lock().length_hint(|obj| obj.len())
696    }
697
698    #[pymethod(magic)]
699    fn reduce(&self, vm: &VirtualMachine) -> PyTupleRef {
700        self.internal
701            .lock()
702            .builtins_iter_reduce(|x| x.clone().into(), vm)
703    }
704
705    #[pymethod(magic)]
706    fn setstate(&self, state: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
707        self.internal
708            .lock()
709            .set_state(state, |obj, pos| pos.min(obj.len()), vm)
710    }
711}
712impl Unconstructible for PyBytesIterator {}
713
714impl SelfIter for PyBytesIterator {}
715impl IterNext for PyBytesIterator {
716    fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
717        zelf.internal.lock().next(|bytes, pos| {
718            Ok(PyIterReturn::from_result(
719                bytes
720                    .as_bytes()
721                    .get(pos)
722                    .map(|&x| vm.new_pyobj(x))
723                    .ok_or(None),
724            ))
725        })
726    }
727}
728
729impl<'a> TryFromBorrowedObject<'a> for PyBytes {
730    fn try_from_borrowed_object(vm: &VirtualMachine, obj: &'a PyObject) -> PyResult<Self> {
731        PyBytesInner::try_from_borrowed_object(vm, obj).map(|x| x.into())
732    }
733}
Morty Proxy This is a proxified and sanitized view of the page, visit original site.