Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

HtmlColor validator #504

Closed
samuelcolvin opened this issue May 3, 2019 · 7 comments

Comments

Projects
None yet
2 participants
@samuelcolvin
Copy link
Owner

commented May 3, 2019

Eg. named colours

or hex, or rgb, or rgba.

I guess we have to use the american spelling for colour. 馃槥

@pilosus

This comment has been minimized.

Copy link
Contributor

commented May 4, 2019

I would suggest using CSS3 spec colour names. They are also defined in SVG spec.

I also have got a couple of questions regarding this issue:

  1. Should the validator only validate that a colour given is among the set of the named colours?
  2. What formats exactly we should support?

This is what I've come up with:

from pydantic import BaseModel, HtmlColor

class Model(BaseModel):
    color: HtmlColor = None


# all these are valid colours, actually variant represent 'antiquewhite'
name_color = Model(color='antiquewhite')
name_color2 = Model(color='AntiqueWhite')  # case ignored
hex_color = Model(color='0xfaebd7')
hex_color2 = Model(color='#faebd7') # also hex
hex_color3 = Model(color='#FAEBD7') # letter's case ignored
rgb_color = Model(color=(250, 235, 215))
# we just ignore alpha-channel if first 3 ints match
rgba_color = Model(color=(250, 235, 215, 10))  

Should we support RGB/RGBA as stings? Something else I'm missing?

@samuelcolvin

This comment has been minimized.

Copy link
Owner Author

commented May 4, 2019

all those cases look good a few more we should probably accept:

  • #fff eg. three letter html colours
  • rgb(1,1,1) and rgba(1,1,1,0) should also work, shouldn't be too hard with a regex

It looks like you can also definite colours using hsl(...) and cmyk(...) but I really think that's too niche for now.

The other case is how does the parsed value m.color behave, eg. is it just a string or an rgb tuple?

I think best that we return an instance of HtmlColor which doesn't inherit from string, but where the __str__ method returns a valid html string.

Another question: should we always validate to the same thing or leave the output format as it came in? Eg. if someone enters rgb(0, 0, 0) should we return:

  • rgb(0, 0, 0)
  • #000 or #000000
  • or black
    ?

Genuinely not sure what's best here. I think we have to have:

  • original() which returns the original value passed to the model
  • as_hex() - short if short makes sense
  • as_rgba() - perhaps either rgb or rgba if there's an alpha channel.
  • as_tuple() - should this be 3 or 4 values or always 4?
  • as_named_color()
  • and perhaps best() (better name needed) which tries named colour but falls back to hex if no name or rgba if there's an alpha channel
  • should __str__ be an alias of original() or best()?

Turns out this is much more complicated than I thought, but quite fun :-)

@pilosus

This comment has been minimized.

Copy link
Contributor

commented May 4, 2019

@samuelcolvin hsl(...) is easy to implement using colorsys from the standard library, though some conversion will be needed as colorsys gets rgb as all floats of range 0.0..1.0. colorsys also lacks cmyk(..).

By the way, it seems that defining rgb/rgba as a tuple of all floats is quite common. So this probably should be supported too, e.g. rgb(0.1, 0.3, 0.2) and rgba(0.1, 0.3, 0.2, 0.1)

Another thing that I've just realised is that if we do all these fancy conversions, we have to take care of alpha composition. We cannot just drop alpha from rgba and match rgb against a set of allowed colours (except for the case where alpha = 1.0). There should be a conversion rgba->rgba done first (see this post for more details).

Another question: should we always validate to the same thing or leave the output format as it came in?

I would certainly expect the output format match the input's one. I think a user can add conversion to the needed format at a post validation step.

as_rgba()

What if as_rgba() always returns rgba (with alpha = 1.0 in rgb case). Then we also add as_rgb() method to only return rgb (there will be a conversion for rgba case here).

as_tuple()

May be also splitted as as_rgb_tuple() and as_rgba_tuple()?

as_named_color()

Return a lower cased name of the colour passed

best() [...] tries named colour but falls back to hex if no name or rgba if there's an alpha channel

This brings us to the first question I've asked: if we only validate that a colour passed is in the set of predefined colours defined in CSS3/SVG specification? Or we just try to validate a colour returning it's predifined name if we can find it, otherwise we fall back to hex or rgba?

should str be an alias of original() or best()?

I woud go for an alias of original()

I think it's also worth taking a look at the webcolors package as well as maplotlib.color API

@samuelcolvin

This comment has been minimized.

Copy link
Owner Author

commented May 4, 2019

This brings us to the first question I've asked: if we only validate that a colour passed is in the set of predefined colours defined in CSS3/SVG specification?

Yes, we're checking it's in the list of predefined colours from css3.

We don't want to do any clever stuff with the alpha channel, if it's there (and not 1.0/255) the colour has to be defined as rgba(...), no hex, no names.

We can happily ignore cmyk, look at how github interprets hex, rgb, rgba and hsl but not cmyk.

as_rgb and as_hex should fail if there's an alpha channel, same as as_named_color should fail if there's no such named colour.

May be also splitted as as_rgb_tuple() and as_rgba_tuple()?

Let's just call it as_tuple and add a keyword argument alpha which can be include, exclude or auto, default to auto.

@samuelcolvin

This comment has been minimized.

Copy link
Owner Author

commented May 4, 2019

maybe we should call the field Color since it's not just html, but also css, svg and many other related technologies.

@pilosus

This comment has been minimized.

Copy link
Contributor

commented May 4, 2019

@samuelcolvin ok, now it's clear. Also agree with Color!

I would work on this issue if you don't mind

@samuelcolvin

This comment has been minimized.

Copy link
Owner Author

commented May 4, 2019

yes please.

For clarity I think Color should only need three slots:

class Color:
    __slots__ = '_rgba', '_original', '_color_match'

    def __init__(...):
        self._rgba: Tuple[int, int, int, Optional[int]] = ...
        self._original: str = ...
        self._color_match: Union[str, None, bool] = None # used to cache the named color or that there is none

Obviously do it how you think best, but I think the above makes sense as a starting point.

pilosus added a commit to pilosus/pydantic that referenced this issue May 6, 2019

@pilosus pilosus referenced this issue May 6, 2019

Closed

Html colors #509

4 of 4 tasks complete

pilosus added a commit to pilosus/pydantic that referenced this issue May 6, 2019

pilosus added a commit to pilosus/pydantic that referenced this issue May 7, 2019

pilosus added a commit to pilosus/pydantic that referenced this issue May 9, 2019

pilosus added a commit to pilosus/pydantic that referenced this issue May 9, 2019

pilosus added a commit to pilosus/pydantic that referenced this issue May 9, 2019

@samuelcolvin samuelcolvin referenced this issue May 11, 2019

Merged

Colors #516

4 of 4 tasks complete

samuelcolvin added a commit that referenced this issue May 21, 2019

samuelcolvin added a commit that referenced this issue May 21, 2019

samuelcolvin added a commit that referenced this issue May 21, 2019

samuelcolvin added a commit that referenced this issue May 22, 2019

Colors (#516)
* Color validator MVP

* Color Validator refactored

* Small optimizations applied

* Code coverage improved

* hex processing improved, json encoder and repr added

* Add documentation, update HISTORY (#504)

* Increase test coverage (#504)

* hex helpers refactored (#504)

* Fixes after code review (#509)

* Color Type section in docs reduced

* Check for valid but unnamed colors

* Minor fixes: typo in docs, default value for float comparison function

* rewrite Color

* fix color tests

* tweaks and improve docs

* tweaks and change Color.__str__

* add as_hsla and as_hsl

* support more rgb(a) formats

* add hsl parsing

* parsing hex with alpha channel

* fix hsl parsing

* simpler failure/fallback rules for color display

* storage as floats internally

* tweak docs

* fix type hints
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can鈥檛 perform that action at this time.