cars: remove CAR_INFO map (#31739)

* no carinfo map

* smaller diff

* not on mock
old-commit-hash: 25ccb2426c
chrysler-long2
Justin Newberry 1 year ago committed by GitHub
parent e68f11ef60
commit e6dbf6d614
  1. 8
      selfdrive/car/__init__.py
  2. 2
      selfdrive/car/body/values.py
  3. 2
      selfdrive/car/chrysler/values.py
  4. 8
      selfdrive/car/docs.py
  5. 2
      selfdrive/car/ford/values.py
  6. 2
      selfdrive/car/gm/values.py
  7. 2
      selfdrive/car/honda/values.py
  8. 2
      selfdrive/car/hyundai/values.py
  9. 2
      selfdrive/car/mazda/values.py
  10. 2
      selfdrive/car/mock/values.py
  11. 2
      selfdrive/car/nissan/values.py
  12. 1
      selfdrive/car/subaru/values.py
  13. 2
      selfdrive/car/tesla/values.py
  14. 7
      selfdrive/car/tests/test_docs.py
  15. 4
      selfdrive/car/tests/test_platform_configs.py
  16. 2
      selfdrive/car/toyota/values.py
  17. 14
      selfdrive/car/values.py
  18. 2
      selfdrive/car/volkswagen/values.py
  19. 6
      tools/cabana/dbc/generate_dbc_json.py
  20. 174
      tools/car_porting/examples/subaru_fuzzy_fingerprint.ipynb

@ -299,14 +299,14 @@ class Platforms(str, ReprEnum):
def create_dbc_map(cls) -> dict[str, DbcDict]: def create_dbc_map(cls) -> dict[str, DbcDict]:
return {p: p.config.dbc_dict for p in cls} return {p: p.config.dbc_dict for p in cls}
@classmethod
def create_carinfo_map(cls) -> dict[str, CarInfos]:
return {p: p.config.car_info for p in cls}
@classmethod @classmethod
def with_flags(cls, flags: IntFlag) -> set['Platforms']: def with_flags(cls, flags: IntFlag) -> set['Platforms']:
return {p for p in cls if p.config.flags & flags} return {p for p in cls if p.config.flags & flags}
@classmethod
def without_flags(cls, flags: IntFlag) -> set['Platforms']:
return {p for p in cls if not (p.config.flags & flags)}
@classmethod @classmethod
def print_debug(cls, flags): def print_debug(cls, flags):
platforms_with_flag = defaultdict(list) platforms_with_flag = defaultdict(list)

@ -38,5 +38,5 @@ FW_QUERY_CONFIG = FwQueryConfig(
], ],
) )
CAR_INFO = CAR.create_carinfo_map()
DBC = CAR.create_dbc_map() DBC = CAR.create_dbc_map()

@ -163,5 +163,5 @@ FW_QUERY_CONFIG = FwQueryConfig(
], ],
) )
CAR_INFO = CAR.create_carinfo_map()
DBC = CAR.create_dbc_map() DBC = CAR.create_dbc_map()

@ -11,6 +11,7 @@ from openpilot.common.basedir import BASEDIR
from openpilot.selfdrive.car import gen_empty_fingerprint from openpilot.selfdrive.car import gen_empty_fingerprint
from openpilot.selfdrive.car.docs_definitions import CarInfo, Column, CommonFootnote, PartType from openpilot.selfdrive.car.docs_definitions import CarInfo, Column, CommonFootnote, PartType
from openpilot.selfdrive.car.car_helpers import interfaces, get_interface_attr from openpilot.selfdrive.car.car_helpers import interfaces, get_interface_attr
from openpilot.selfdrive.car.values import PLATFORMS
def get_all_footnotes() -> dict[Enum, int]: def get_all_footnotes() -> dict[Enum, int]:
@ -27,9 +28,10 @@ CARS_MD_TEMPLATE = os.path.join(BASEDIR, "selfdrive", "car", "CARS_template.md")
def get_all_car_info() -> list[CarInfo]: def get_all_car_info() -> list[CarInfo]:
all_car_info: list[CarInfo] = [] all_car_info: list[CarInfo] = []
footnotes = get_all_footnotes() footnotes = get_all_footnotes()
for model, car_info in get_interface_attr("CAR_INFO", combine_brands=True).items(): for model, platform in PLATFORMS.items():
car_info = platform.config.car_info
# If available, uses experimental longitudinal limits for the docs # If available, uses experimental longitudinal limits for the docs
CP = interfaces[model][0].get_params(model, fingerprint=gen_empty_fingerprint(), CP = interfaces[model][0].get_params(platform, fingerprint=gen_empty_fingerprint(),
car_fw=[car.CarParams.CarFw(ecu="unknown")], experimental_long=True, docs=True) car_fw=[car.CarParams.CarFw(ecu="unknown")], experimental_long=True, docs=True)
if CP.dashcamOnly or car_info is None: if CP.dashcamOnly or car_info is None:
@ -37,7 +39,7 @@ def get_all_car_info() -> list[CarInfo]:
# A platform can include multiple car models # A platform can include multiple car models
if not isinstance(car_info, list): if not isinstance(car_info, list):
car_info = (car_info,) car_info = [car_info,]
for _car_info in car_info: for _car_info in car_info:
if not hasattr(_car_info, "row"): if not hasattr(_car_info, "row"):

@ -209,5 +209,5 @@ FW_QUERY_CONFIG = FwQueryConfig(
], ],
) )
CAR_INFO = CAR.create_carinfo_map()
DBC = CAR.create_dbc_map() DBC = CAR.create_dbc_map()

@ -247,5 +247,5 @@ CAMERA_ACC_CAR = {CAR.BOLT_EUV, CAR.SILVERADO, CAR.EQUINOX, CAR.TRAILBLAZER}
STEER_THRESHOLD = 1.0 STEER_THRESHOLD = 1.0
CAR_INFO = CAR.create_carinfo_map()
DBC = CAR.create_dbc_map() DBC = CAR.create_dbc_map()

@ -352,5 +352,5 @@ HONDA_NIDEC_ALT_SCM_MESSAGES = CAR.with_flags(HondaFlags.NIDEC_ALT_SCM_MESSAGES)
HONDA_BOSCH = CAR.with_flags(HondaFlags.BOSCH) HONDA_BOSCH = CAR.with_flags(HondaFlags.BOSCH)
HONDA_BOSCH_RADARLESS = CAR.with_flags(HondaFlags.BOSCH_RADARLESS) HONDA_BOSCH_RADARLESS = CAR.with_flags(HondaFlags.BOSCH_RADARLESS)
CAR_INFO = CAR.create_carinfo_map()
DBC = CAR.create_dbc_map() DBC = CAR.create_dbc_map()

@ -847,7 +847,7 @@ LEGACY_SAFETY_MODE_CAR = CAR.with_flags(HyundaiFlags.LEGACY)
UNSUPPORTED_LONGITUDINAL_CAR = CAR.with_flags(HyundaiFlags.LEGACY) | CAR.with_flags(HyundaiFlags.UNSUPPORTED_LONGITUDINAL) UNSUPPORTED_LONGITUDINAL_CAR = CAR.with_flags(HyundaiFlags.LEGACY) | CAR.with_flags(HyundaiFlags.UNSUPPORTED_LONGITUDINAL)
CAR_INFO = CAR.create_carinfo_map()
DBC = CAR.create_dbc_map() DBC = CAR.create_dbc_map()
if __name__ == "__main__": if __name__ == "__main__":

@ -108,5 +108,5 @@ FW_QUERY_CONFIG = FwQueryConfig(
) )
CAR_INFO = CAR.create_carinfo_map()
DBC = CAR.create_dbc_map() DBC = CAR.create_dbc_map()

@ -11,4 +11,4 @@ class CAR(Platforms):
) )
CAR_INFO = CAR.create_carinfo_map()

@ -63,7 +63,7 @@ class CAR(Platforms):
) )
CAR_INFO = CAR.create_carinfo_map()
DBC = CAR.create_dbc_map() DBC = CAR.create_dbc_map()
# Default diagnostic session # Default diagnostic session

@ -263,7 +263,6 @@ FW_QUERY_CONFIG = FwQueryConfig(
} }
) )
CAR_INFO = CAR.create_carinfo_map()
DBC = CAR.create_dbc_map() DBC = CAR.create_dbc_map()

@ -92,5 +92,5 @@ class CarControllerParams:
pass pass
CAR_INFO = CAR.create_carinfo_map()
DBC = CAR.create_dbc_map() DBC = CAR.create_dbc_map()

@ -5,10 +5,11 @@ import re
import unittest import unittest
from openpilot.common.basedir import BASEDIR from openpilot.common.basedir import BASEDIR
from openpilot.selfdrive.car.car_helpers import interfaces, get_interface_attr from openpilot.selfdrive.car.car_helpers import interfaces
from openpilot.selfdrive.car.docs import CARS_MD_OUT, CARS_MD_TEMPLATE, generate_cars_md, get_all_car_info from openpilot.selfdrive.car.docs import CARS_MD_OUT, CARS_MD_TEMPLATE, generate_cars_md, get_all_car_info
from openpilot.selfdrive.car.docs_definitions import Cable, Column, PartType, Star from openpilot.selfdrive.car.docs_definitions import Cable, Column, PartType, Star
from openpilot.selfdrive.car.honda.values import CAR as HONDA from openpilot.selfdrive.car.honda.values import CAR as HONDA
from openpilot.selfdrive.car.values import PLATFORMS
from openpilot.selfdrive.debug.dump_car_info import dump_car_info from openpilot.selfdrive.debug.dump_car_info import dump_car_info
from openpilot.selfdrive.debug.print_docs_diff import print_car_info_diff from openpilot.selfdrive.debug.print_docs_diff import print_car_info_diff
@ -42,10 +43,10 @@ class TestCarDocs(unittest.TestCase):
make_model_years[make_model].append(year) make_model_years[make_model].append(year)
def test_missing_car_info(self): def test_missing_car_info(self):
all_car_info_platforms = get_interface_attr("CAR_INFO", combine_brands=True).keys() all_car_info_platforms = [name for name, config in PLATFORMS.items()]
for platform in sorted(interfaces.keys()): for platform in sorted(interfaces.keys()):
with self.subTest(platform=platform): with self.subTest(platform=platform):
self.assertTrue(platform in all_car_info_platforms, f"Platform: {platform} doesn't exist in CarInfo") self.assertTrue(platform in all_car_info_platforms, f"Platform: {platform} doesn't have a CarInfo entry")
def test_naming_conventions(self): def test_naming_conventions(self):
# Asserts market-standard car naming conventions by brand # Asserts market-standard car naming conventions by brand

@ -11,7 +11,9 @@ class TestPlatformConfigs(unittest.TestCase):
for platform in PLATFORMS.values(): for platform in PLATFORMS.values():
with self.subTest(platform=str(platform)): with self.subTest(platform=str(platform)):
self.assertTrue(platform.config._frozen) self.assertTrue(platform.config._frozen)
self.assertIn("pt", platform.config.dbc_dict)
if platform != "mock":
self.assertIn("pt", platform.config.dbc_dict)
self.assertTrue(len(platform.config.platform_str) > 0) self.assertTrue(len(platform.config.platform_str) > 0)
self.assertIsNotNone(platform.config.specs) self.assertIsNotNone(platform.config.specs)

@ -608,5 +608,5 @@ ANGLE_CONTROL_CAR = CAR.with_flags(ToyotaFlags.ANGLE_CONTROL)
# no resume button press required # no resume button press required
NO_STOP_TIMER_CAR = CAR.with_flags(ToyotaFlags.NO_STOP_TIMER) NO_STOP_TIMER_CAR = CAR.with_flags(ToyotaFlags.NO_STOP_TIMER)
CAR_INFO = CAR.create_carinfo_map()
DBC = CAR.create_dbc_map() DBC = CAR.create_dbc_map()

@ -1,4 +1,4 @@
from typing import cast from typing import Any, Callable, cast
from openpilot.selfdrive.car.body.values import CAR as BODY from openpilot.selfdrive.car.body.values import CAR as BODY
from openpilot.selfdrive.car.chrysler.values import CAR as CHRYSLER from openpilot.selfdrive.car.chrysler.values import CAR as CHRYSLER
from openpilot.selfdrive.car.ford.values import CAR as FORD from openpilot.selfdrive.car.ford.values import CAR as FORD
@ -6,13 +6,21 @@ from openpilot.selfdrive.car.gm.values import CAR as GM
from openpilot.selfdrive.car.honda.values import CAR as HONDA from openpilot.selfdrive.car.honda.values import CAR as HONDA
from openpilot.selfdrive.car.hyundai.values import CAR as HYUNDAI from openpilot.selfdrive.car.hyundai.values import CAR as HYUNDAI
from openpilot.selfdrive.car.mazda.values import CAR as MAZDA from openpilot.selfdrive.car.mazda.values import CAR as MAZDA
from openpilot.selfdrive.car.mock.values import CAR as MOCK
from openpilot.selfdrive.car.nissan.values import CAR as NISSAN from openpilot.selfdrive.car.nissan.values import CAR as NISSAN
from openpilot.selfdrive.car.subaru.values import CAR as SUBARU from openpilot.selfdrive.car.subaru.values import CAR as SUBARU
from openpilot.selfdrive.car.tesla.values import CAR as TESLA from openpilot.selfdrive.car.tesla.values import CAR as TESLA
from openpilot.selfdrive.car.toyota.values import CAR as TOYOTA from openpilot.selfdrive.car.toyota.values import CAR as TOYOTA
from openpilot.selfdrive.car.volkswagen.values import CAR as VOLKSWAGEN from openpilot.selfdrive.car.volkswagen.values import CAR as VOLKSWAGEN
Platform = BODY | CHRYSLER | FORD | GM | HONDA | HYUNDAI | MAZDA | NISSAN | SUBARU | TESLA | TOYOTA | VOLKSWAGEN Platform = BODY | CHRYSLER | FORD | GM | HONDA | HYUNDAI | MAZDA | MOCK | NISSAN | SUBARU | TESLA | TOYOTA | VOLKSWAGEN
BRANDS = [BODY, CHRYSLER, FORD, GM, HONDA, HYUNDAI, MAZDA, NISSAN, SUBARU, TESLA, TOYOTA, VOLKSWAGEN] BRANDS = [BODY, CHRYSLER, FORD, GM, HONDA, HYUNDAI, MAZDA, MOCK, NISSAN, SUBARU, TESLA, TOYOTA, VOLKSWAGEN]
PLATFORMS: dict[str, Platform] = {str(platform): platform for brand in BRANDS for platform in cast(list[Platform], brand)} PLATFORMS: dict[str, Platform] = {str(platform): platform for brand in BRANDS for platform in cast(list[Platform], brand)}
MapFunc = Callable[[Platform], Any]
def create_platform_map(func: MapFunc):
return {str(platform): func(platform) for platform in PLATFORMS.values() if func(platform) is not None}

@ -416,5 +416,5 @@ FW_QUERY_CONFIG = FwQueryConfig(
extra_ecus=[(Ecu.fwdCamera, 0x74f, None)], extra_ecus=[(Ecu.fwdCamera, 0x74f, None)],
) )
CAR_INFO = CAR.create_carinfo_map()
DBC = CAR.create_dbc_map() DBC = CAR.create_dbc_map()

@ -2,13 +2,11 @@
import argparse import argparse
import json import json
from openpilot.selfdrive.car.car_helpers import get_interface_attr from openpilot.selfdrive.car.values import create_platform_map
def generate_dbc_json() -> str: def generate_dbc_json() -> str:
all_cars_by_brand = get_interface_attr("CAR_INFO") dbc_map = create_platform_map(lambda platform: platform.config.dbc_dict["pt"] if platform != "mock" else None)
all_dbcs_by_brand = get_interface_attr("DBC")
dbc_map = {car: all_dbcs_by_brand[brand][car]['pt'] for brand, cars in all_cars_by_brand.items() for car in cars if car != 'mock'}
return json.dumps(dict(sorted(dbc_map.items())), indent=2) return json.dumps(dict(sorted(dbc_map.items())), indent=2)

@ -2,15 +2,23 @@
"cells": [ "cells": [
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 148, "execution_count": 1,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"kj/filesystem-disk-unix.c++:1703: warning: PWD environment variable doesn't match current directory; pwd = /home/batman\n"
]
}
],
"source": [ "source": [
"from cereal import car\n", "from cereal import car\n",
"from openpilot.selfdrive.car.subaru.values import CAR, PREGLOBAL_CARS\n", "from openpilot.selfdrive.car.subaru.values import CAR, SubaruFlags\n",
"from openpilot.selfdrive.car.subaru.fingerprints import FW_VERSIONS\n", "from openpilot.selfdrive.car.subaru.fingerprints import FW_VERSIONS\n",
"\n", "\n",
"TEST_PLATFORMS = set(c.value for c in CAR) - PREGLOBAL_CARS # preglobal cars seem to have a different format for fingerprints, ignore for now\n", "TEST_PLATFORMS = CAR.without_flags(SubaruFlags.PREGLOBAL)\n",
"\n", "\n",
"Ecu = car.CarParams.Ecu\n", "Ecu = car.CarParams.Ecu\n",
"\n", "\n",
@ -19,22 +27,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 149, "execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"from openpilot.selfdrive.car.subaru.values import CAR_INFO\n",
"\n",
"def get_carinfo(model: CAR):\n",
" c = CAR_INFO[model]\n",
" if isinstance(c, list):\n",
" c = c[0]\n",
" return c"
]
},
{
"cell_type": "code",
"execution_count": 150,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@ -66,7 +59,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 151, "execution_count": 3,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@ -90,7 +83,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 152, "execution_count": 4,
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
{ {
@ -103,38 +96,9 @@
"SUBARU IMPREZA LIMITED 2019 0c not in dict_keys([b'\\x18', b'\\x19', b' ', b'!', b'\"', b'#'])\n", "SUBARU IMPREZA LIMITED 2019 0c not in dict_keys([b'\\x18', b'\\x19', b' ', b'!', b'\"', b'#'])\n",
"SUBARU IMPREZA LIMITED 2019 2e not in dict_keys([b'\\x18', b'\\x19', b' ', b'!', b'\"', b'#'])\n", "SUBARU IMPREZA LIMITED 2019 2e not in dict_keys([b'\\x18', b'\\x19', b' ', b'!', b'\"', b'#'])\n",
"SUBARU IMPREZA LIMITED 2019 3f not in dict_keys([b'\\x18', b'\\x19', b' ', b'!', b'\"', b'#'])\n", "SUBARU IMPREZA LIMITED 2019 3f not in dict_keys([b'\\x18', b'\\x19', b' ', b'!', b'\"', b'#'])\n",
"correct_year=True platform=SUBARU ASCENT LIMITED 2019 year=2019 years=[2019, 2020, 2021]\n",
"correct_year=True platform=SUBARU ASCENT LIMITED 2019 year=2021 years=[2019, 2020, 2021]\n",
"correct_year=False platform=SUBARU IMPREZA SPORT 2020 year=2019 years=[2020, 2021, 2022]\n",
"correct_year=False platform=SUBARU IMPREZA SPORT 2020 year=2019 years=[2020, 2021, 2022]\n",
"correct_year=True platform=SUBARU IMPREZA SPORT 2020 year=2020 years=[2020, 2021, 2022]\n",
"correct_year=True platform=SUBARU IMPREZA SPORT 2020 year=2021 years=[2020, 2021, 2022]\n",
"correct_year=True platform=SUBARU IMPREZA SPORT 2020 year=2021 years=[2020, 2021, 2022]\n",
"correct_year=True platform=SUBARU IMPREZA SPORT 2020 year=2021 years=[2020, 2021, 2022]\n",
"correct_year=False platform=SUBARU FORESTER 2019 year=2018 years=[2019, 2020, 2021]\n",
"correct_year=False platform=SUBARU FORESTER 2019 year=2018 years=[2019, 2020, 2021]\n",
"correct_year=True platform=SUBARU FORESTER 2019 year=2019 years=[2019, 2020, 2021]\n",
"correct_year=True platform=SUBARU FORESTER 2019 year=2019 years=[2019, 2020, 2021]\n",
"correct_year=True platform=SUBARU FORESTER 2019 year=2020 years=[2019, 2020, 2021]\n",
"correct_year=True platform=SUBARU FORESTER 2019 year=2020 years=[2019, 2020, 2021]\n",
"correct_year=True platform=SUBARU IMPREZA LIMITED 2019 year=2019 years=[2017, 2018, 2019]\n",
"correct_year=True platform=SUBARU IMPREZA LIMITED 2019 year=2019 years=[2017, 2018, 2019]\n",
"correct_year=True platform=SUBARU IMPREZA LIMITED 2019 year=2018 years=[2017, 2018, 2019]\n",
"correct_year=True platform=SUBARU IMPREZA LIMITED 2019 year=2019 years=[2017, 2018, 2019]\n",
"correct_year=True platform=SUBARU IMPREZA LIMITED 2019 year=2019 years=[2017, 2018, 2019]\n",
"correct_year=True platform=SUBARU IMPREZA LIMITED 2019 year=2019 years=[2017, 2018, 2019]\n",
"correct_year=True platform=SUBARU OUTBACK 7TH GEN year=2023 years=[2023]\n", "correct_year=True platform=SUBARU OUTBACK 7TH GEN year=2023 years=[2023]\n",
"correct_year=True platform=SUBARU OUTBACK 7TH GEN year=2023 years=[2023]\n", "correct_year=True platform=SUBARU OUTBACK 7TH GEN year=2023 years=[2023]\n",
"correct_year=True platform=SUBARU LEGACY 7TH GEN year=2020 years=[2020, 2021, 2022]\n", "correct_year=True platform=SUBARU OUTBACK 6TH GEN year=2020 years=[2020, 2021, 2022]\n",
"correct_year=True platform=SUBARU LEGACY 7TH GEN year=2020 years=[2020, 2021, 2022]\n",
"correct_year=True platform=SUBARU LEGACY 7TH GEN year=2020 years=[2020, 2021, 2022]\n",
"correct_year=True platform=SUBARU LEGACY 7TH GEN year=2020 years=[2020, 2021, 2022]\n",
"correct_year=False platform=SUBARU FORESTER 2022 year=2021 years=[2022, 2023]\n",
"correct_year=False platform=SUBARU FORESTER 2022 year=2021 years=[2022, 2023]\n",
"correct_year=True platform=SUBARU FORESTER 2022 year=2022 years=[2022, 2023]\n",
"correct_year=True platform=SUBARU FORESTER 2022 year=2022 years=[2022, 2023]\n",
"correct_year=False platform=SUBARU CROSSTREK HYBRID 2020 year=2019 years=[2020]\n",
"correct_year=False platform=SUBARU CROSSTREK HYBRID 2020 year=2021 years=[2020]\n",
"correct_year=True platform=SUBARU OUTBACK 6TH GEN year=2020 years=[2020, 2021, 2022]\n", "correct_year=True platform=SUBARU OUTBACK 6TH GEN year=2020 years=[2020, 2021, 2022]\n",
"correct_year=True platform=SUBARU OUTBACK 6TH GEN year=2020 years=[2020, 2021, 2022]\n", "correct_year=True platform=SUBARU OUTBACK 6TH GEN year=2020 years=[2020, 2021, 2022]\n",
"correct_year=True platform=SUBARU OUTBACK 6TH GEN year=2020 years=[2020, 2021, 2022]\n", "correct_year=True platform=SUBARU OUTBACK 6TH GEN year=2020 years=[2020, 2021, 2022]\n",
@ -145,14 +109,47 @@
"correct_year=True platform=SUBARU OUTBACK 6TH GEN year=2020 years=[2020, 2021, 2022]\n", "correct_year=True platform=SUBARU OUTBACK 6TH GEN year=2020 years=[2020, 2021, 2022]\n",
"correct_year=True platform=SUBARU OUTBACK 6TH GEN year=2022 years=[2020, 2021, 2022]\n", "correct_year=True platform=SUBARU OUTBACK 6TH GEN year=2022 years=[2020, 2021, 2022]\n",
"correct_year=True platform=SUBARU OUTBACK 6TH GEN year=2022 years=[2020, 2021, 2022]\n", "correct_year=True platform=SUBARU OUTBACK 6TH GEN year=2022 years=[2020, 2021, 2022]\n",
"correct_year=False platform=SUBARU CROSSTREK HYBRID 2020 year=2019 years=[2020]\n",
"correct_year=False platform=SUBARU CROSSTREK HYBRID 2020 year=2021 years=[2020]\n",
"correct_year=False platform=SUBARU FORESTER HYBRID 2020 year=2019 years=[2020]\n",
"correct_year=True platform=SUBARU LEGACY 7TH GEN year=2020 years=[2020, 2021, 2022]\n",
"correct_year=True platform=SUBARU LEGACY 7TH GEN year=2020 years=[2020, 2021, 2022]\n",
"correct_year=True platform=SUBARU LEGACY 7TH GEN year=2020 years=[2020, 2021, 2022]\n",
"correct_year=True platform=SUBARU LEGACY 7TH GEN year=2020 years=[2020, 2021, 2022]\n",
"correct_year=True platform=SUBARU IMPREZA LIMITED 2019 year=2019 years=[2017, 2018, 2019]\n",
"correct_year=True platform=SUBARU IMPREZA LIMITED 2019 year=2019 years=[2017, 2018, 2019]\n",
"correct_year=True platform=SUBARU IMPREZA LIMITED 2019 year=2018 years=[2017, 2018, 2019]\n",
"correct_year=True platform=SUBARU IMPREZA LIMITED 2019 year=2019 years=[2017, 2018, 2019]\n",
"correct_year=True platform=SUBARU IMPREZA LIMITED 2019 year=2019 years=[2017, 2018, 2019]\n",
"correct_year=True platform=SUBARU IMPREZA LIMITED 2019 year=2019 years=[2017, 2018, 2019]\n",
"correct_year=False platform=SUBARU FORESTER 2022 year=2021 years=[2022, 2023, 2024]\n",
"correct_year=False platform=SUBARU FORESTER 2022 year=2021 years=[2022, 2023, 2024]\n",
"correct_year=True platform=SUBARU FORESTER 2022 year=2022 years=[2022, 2023, 2024]\n",
"correct_year=True platform=SUBARU FORESTER 2022 year=2022 years=[2022, 2023, 2024]\n",
"correct_year=False platform=SUBARU IMPREZA SPORT 2020 year=2019 years=[2020, 2021, 2022]\n",
"correct_year=False platform=SUBARU IMPREZA SPORT 2020 year=2019 years=[2020, 2021, 2022]\n",
"correct_year=True platform=SUBARU IMPREZA SPORT 2020 year=2020 years=[2020, 2021, 2022]\n",
"correct_year=True platform=SUBARU IMPREZA SPORT 2020 year=2021 years=[2020, 2021, 2022]\n",
"correct_year=True platform=SUBARU IMPREZA SPORT 2020 year=2021 years=[2020, 2021, 2022]\n",
"correct_year=True platform=SUBARU IMPREZA SPORT 2020 year=2021 years=[2020, 2021, 2022]\n",
"correct_year=True platform=SUBARU ASCENT 2023 year=2023 years=[2023]\n", "correct_year=True platform=SUBARU ASCENT 2023 year=2023 years=[2023]\n",
"correct_year=False platform=SUBARU FORESTER HYBRID 2020 year=2019 years=[2020]\n" "correct_year=True platform=SUBARU ASCENT LIMITED 2019 year=2019 years=[2019, 2020, 2021]\n",
"correct_year=True platform=SUBARU ASCENT LIMITED 2019 year=2021 years=[2019, 2020, 2021]\n",
"correct_year=False platform=SUBARU FORESTER 2019 year=2018 years=[2019, 2020, 2021]\n",
"correct_year=False platform=SUBARU FORESTER 2019 year=2018 years=[2019, 2020, 2021]\n",
"correct_year=True platform=SUBARU FORESTER 2019 year=2019 years=[2019, 2020, 2021]\n",
"correct_year=True platform=SUBARU FORESTER 2019 year=2019 years=[2019, 2020, 2021]\n",
"correct_year=True platform=SUBARU FORESTER 2019 year=2020 years=[2019, 2020, 2021]\n",
"correct_year=True platform=SUBARU FORESTER 2019 year=2020 years=[2019, 2020, 2021]\n"
] ]
} }
], ],
"source": [ "source": [
"def test_year_code(platform, year):\n", "def test_year_code(platform, year):\n",
" years = [int(y) for y in get_carinfo(platform).year_list]\n", " car_info = CAR(platform).config.car_info\n",
" if isinstance(car_info, list):\n",
" car_info = car_info[0]\n",
" years = [int(y) for y in car_info.year_list]\n",
" correct_year = year in years\n", " correct_year = year in years\n",
" print(f\"{correct_year=!s: <6} {platform=: <32} {year=: <5} {years=}\")\n", " print(f\"{correct_year=!s: <6} {platform=: <32} {year=: <5} {years=}\")\n",
"\n", "\n",
@ -163,27 +160,33 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 153, "execution_count": 5,
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
{ {
"name": "stdout", "name": "stdout",
"output_type": "stream", "output_type": "stream",
"text": [ "text": [
"in_possible_platforms=True platform=SUBARU ASCENT LIMITED 2019 platforms=['SUBARU ASCENT LIMITED 2019', 'SUBARU ASCENT 2023']\n", "in_possible_platforms=True platform=SUBARU OUTBACK 7TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n",
"in_possible_platforms=True platform=SUBARU ASCENT LIMITED 2019 platforms=['SUBARU ASCENT LIMITED 2019', 'SUBARU ASCENT 2023']\n", "in_possible_platforms=True platform=SUBARU OUTBACK 7TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n",
"in_possible_platforms=True platform=SUBARU IMPREZA SPORT 2020 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n", "in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n",
"in_possible_platforms=True platform=SUBARU IMPREZA SPORT 2020 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n", "in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n",
"in_possible_platforms=True platform=SUBARU IMPREZA SPORT 2020 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n", "in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n",
"in_possible_platforms=True platform=SUBARU IMPREZA SPORT 2020 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n", "in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n",
"in_possible_platforms=True platform=SUBARU IMPREZA SPORT 2020 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n", "in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n",
"in_possible_platforms=True platform=SUBARU IMPREZA SPORT 2020 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n", "in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n",
"in_possible_platforms=True platform=SUBARU FORESTER 2019 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n", "in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n",
"in_possible_platforms=True platform=SUBARU FORESTER 2019 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n", "in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n",
"in_possible_platforms=True platform=SUBARU FORESTER 2019 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n", "in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n",
"in_possible_platforms=True platform=SUBARU FORESTER 2019 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n", "in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n",
"in_possible_platforms=True platform=SUBARU FORESTER 2019 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n", "in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n",
"in_possible_platforms=True platform=SUBARU FORESTER 2019 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n", "in_possible_platforms=True platform=SUBARU CROSSTREK HYBRID 2020 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n",
"in_possible_platforms=True platform=SUBARU CROSSTREK HYBRID 2020 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n",
"in_possible_platforms=True platform=SUBARU FORESTER HYBRID 2020 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n",
"in_possible_platforms=True platform=SUBARU LEGACY 7TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n",
"in_possible_platforms=True platform=SUBARU LEGACY 7TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n",
"in_possible_platforms=True platform=SUBARU LEGACY 7TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n",
"in_possible_platforms=True platform=SUBARU LEGACY 7TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n",
"in_possible_platforms=True platform=SUBARU IMPREZA LIMITED 2019 platforms=['SUBARU IMPREZA LIMITED 2019']\n", "in_possible_platforms=True platform=SUBARU IMPREZA LIMITED 2019 platforms=['SUBARU IMPREZA LIMITED 2019']\n",
"in_possible_platforms=True platform=SUBARU IMPREZA LIMITED 2019 platforms=['SUBARU IMPREZA LIMITED 2019']\n", "in_possible_platforms=True platform=SUBARU IMPREZA LIMITED 2019 platforms=['SUBARU IMPREZA LIMITED 2019']\n",
"in_possible_platforms=True platform=SUBARU IMPREZA LIMITED 2019 platforms=['SUBARU IMPREZA LIMITED 2019']\n", "in_possible_platforms=True platform=SUBARU IMPREZA LIMITED 2019 platforms=['SUBARU IMPREZA LIMITED 2019']\n",
@ -196,30 +199,25 @@
"in_possible_platforms=True platform=SUBARU IMPREZA LIMITED 2019 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n", "in_possible_platforms=True platform=SUBARU IMPREZA LIMITED 2019 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n",
"in_possible_platforms=True platform=SUBARU IMPREZA LIMITED 2019 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n", "in_possible_platforms=True platform=SUBARU IMPREZA LIMITED 2019 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n",
"in_possible_platforms=True platform=SUBARU IMPREZA LIMITED 2019 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n", "in_possible_platforms=True platform=SUBARU IMPREZA LIMITED 2019 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n",
"in_possible_platforms=True platform=SUBARU OUTBACK 7TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n",
"in_possible_platforms=True platform=SUBARU OUTBACK 7TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n",
"in_possible_platforms=True platform=SUBARU LEGACY 7TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n",
"in_possible_platforms=True platform=SUBARU LEGACY 7TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n",
"in_possible_platforms=True platform=SUBARU LEGACY 7TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n",
"in_possible_platforms=True platform=SUBARU LEGACY 7TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n",
"in_possible_platforms=True platform=SUBARU FORESTER 2022 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n", "in_possible_platforms=True platform=SUBARU FORESTER 2022 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n",
"in_possible_platforms=True platform=SUBARU FORESTER 2022 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n", "in_possible_platforms=True platform=SUBARU FORESTER 2022 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n",
"in_possible_platforms=True platform=SUBARU FORESTER 2022 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n", "in_possible_platforms=True platform=SUBARU FORESTER 2022 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n",
"in_possible_platforms=True platform=SUBARU FORESTER 2022 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n", "in_possible_platforms=True platform=SUBARU FORESTER 2022 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n",
"in_possible_platforms=True platform=SUBARU CROSSTREK HYBRID 2020 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n", "in_possible_platforms=True platform=SUBARU IMPREZA SPORT 2020 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n",
"in_possible_platforms=True platform=SUBARU CROSSTREK HYBRID 2020 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n", "in_possible_platforms=True platform=SUBARU IMPREZA SPORT 2020 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n",
"in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n", "in_possible_platforms=True platform=SUBARU IMPREZA SPORT 2020 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n",
"in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n", "in_possible_platforms=True platform=SUBARU IMPREZA SPORT 2020 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n",
"in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n", "in_possible_platforms=True platform=SUBARU IMPREZA SPORT 2020 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n",
"in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n", "in_possible_platforms=True platform=SUBARU IMPREZA SPORT 2020 platforms=['SUBARU IMPREZA LIMITED 2019', 'SUBARU IMPREZA SPORT 2020', 'SUBARU CROSSTREK HYBRID 2020']\n",
"in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n",
"in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n",
"in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n",
"in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n",
"in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n",
"in_possible_platforms=True platform=SUBARU OUTBACK 6TH GEN platforms=['SUBARU OUTBACK 6TH GEN', 'SUBARU LEGACY 7TH GEN', 'SUBARU OUTBACK 7TH GEN']\n",
"in_possible_platforms=True platform=SUBARU ASCENT 2023 platforms=['SUBARU ASCENT LIMITED 2019', 'SUBARU ASCENT 2023']\n", "in_possible_platforms=True platform=SUBARU ASCENT 2023 platforms=['SUBARU ASCENT LIMITED 2019', 'SUBARU ASCENT 2023']\n",
"in_possible_platforms=True platform=SUBARU FORESTER HYBRID 2020 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n" "in_possible_platforms=True platform=SUBARU ASCENT LIMITED 2019 platforms=['SUBARU ASCENT LIMITED 2019', 'SUBARU ASCENT 2023']\n",
"in_possible_platforms=True platform=SUBARU ASCENT LIMITED 2019 platforms=['SUBARU ASCENT LIMITED 2019', 'SUBARU ASCENT 2023']\n",
"in_possible_platforms=True platform=SUBARU FORESTER 2019 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n",
"in_possible_platforms=True platform=SUBARU FORESTER 2019 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n",
"in_possible_platforms=True platform=SUBARU FORESTER 2019 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n",
"in_possible_platforms=True platform=SUBARU FORESTER 2019 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n",
"in_possible_platforms=True platform=SUBARU FORESTER 2019 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n",
"in_possible_platforms=True platform=SUBARU FORESTER 2019 platforms=['SUBARU FORESTER 2019', 'SUBARU FORESTER HYBRID 2020', 'SUBARU FORESTER 2022']\n"
] ]
} }
], ],

Loading…
Cancel
Save